UWP Why does a style not apply to TargetTypes in DataTemplate? - xaml

Given a style in a Page.Resource:
<Style x:Name="ItemTitle" TargetType="TextBlock">
<Setter Property="FontSize" Value="16"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
</Style>
It is correctly applied to any regular TextBlock on the same page.
However, when I use a DataTemplate for an Item in a GridView on that page, this style does not apply.
<DataTemplate x:Key="Output" x:DataType="vm:Output">
<TextBlock Text="{x:Bind Text}"></TextBlock>
</DataTemplate>
It does work when I apply the style explicitly on the DataTemplate, e.g.:
<DataTemplate x:Key="Output" x:DataType="vm:Output">
<TextBlock Style="{StaticResource ItemTitle}" Text="{x:Bind Text}"></TextBlock>
</DataTemplate>
Does anyone know what's up?

It's expected and intentional. If it doesn't derive from Control (like DataTemplate) then it won't inherit an implicit style unless they're in the application resource dictionaries as global defaults.
Or more specifically;
Templates are viewed as an encapsulation boundary when looking up an implicit style for an element which is not a subtype of Control.
Hope this helps. Cheers.
Addendum:
If it's a situation where you have a lot of the same element nested in a Template you can just set it once and allow it to inherit to all the nested controls of the type like (in pseudo);
<Parent>
<Parent.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource ItemTitle}"/>
<Parent.Resources>
<!-- These will all inherit the Style resource now,
without explicit style setting individually. -->
<TextBlock/>
<TextBlock/>
<TextBlock/>
</Parent>

Related

How to inherit from, or override elements of, the Default style for a WinUI 3 control?

I am trying to learn how to use Styles most effectively in WinUI 3 (from WindowsAppSDK 1.1.1) but I'm having difficulty getting simple inheritance to work.
Consider the NavigationViewItem class. I'd like to modify the default style to bind the FontSize and Height properties. The following works in my Page XAML:
<NavigationViewItem x:Uid="Shell_05" helpers:NavigationHelper.NavigateTo="ViewModels._05CreditViewModel"
FontSize="{Binding ViewModel.RootShellFontSize, ElementName=shellPage}"
Height="{Binding ViewModel.CurrentMenuItemHeight, ElementName=shellPage}">
<NavigationViewItem.Icon>
<BitmapIcon UriSource="\Images\credit.png"/>
</NavigationViewItem.Icon>
</NavigationViewItem>
But adding the two properties to a page resource does not (although the FontSize property works in each of the following. It's the Height that doesn't):
<Page.Resources>
<Style TargetType="NavigationViewItem" >
<Setter Property="FontSize" Value="{Binding ViewModel.RootShellFontSize, ElementName=shellPage}" />
<Setter Property="Height" Value="{Binding ViewModel.CurrentMenuItemHeight, ElementName=shellPage}" />
</Style>
</Page.Resources>
Neither does adding the style to a resource dictionary and merging. I've read over what I can find about inheriting styles and the BasedOn="" extension is an explicit way to derive from an existing style in WinUI versions prior to 2.6 (I think). Apparently, WinUI 3 does not require BasedOn. In any case, simply specifying TargetType="NavigationViewItem" doesn't work, but nor does
<Style TargetType="controls:NavigationViewItem" BasedOn="DefaultNavigationViewItemStyle">
The source code for v1.1.1 of the SDK declares a default style for the NavigationViewItem in generic.xaml, but there is no definition for DefaultNavigationViewItemStyle.
I also cannot derive from the default style using
<Style TargetType="controls:NavigationViewItem" BasedOn="{StaticResource {x:Type NavigationViewItem}}">
because x:Type is undefined.
I can do all of the bindings I want in code but I assume it's both clearer and more efficient to do it in XAML.
How do I inherit, derive from, or override a portion of the default style for a WinUI 3 control (not a custom control) in a desktop application, please?
Thanks for any help. Pointers to good XAML for WinUI 3 documentation (or books and articles) would also be greatly appreciated.
In your case the height is most probably not working since page.resources get compiled before object initialization and the height of the CurrentMenuItemHeight is 0. To solve it just set the mode to one way as such
{Binding ViewModel.CurrentMenuItemHeight,Mode=OneWay , ElementName=shellPage}
When you wish to use BasedOn, just say BasedOn={ThemeResource styleName}.
Just make sure the style is actually defined in Generic.xaml file which u can find in "C:\Users\AdminName.nuget\packages\microsoft.windowsappsdk\1.1.1\lib\uap10.0\Microsoft.UI\Themes"
So your final page.resources should be as such:
<Page.Resources>
<Style TargetType="NavigationViewItem" >
<Setter Property="FontSize" Value="{Binding ViewModel.RootShellFontSize, ElementName=shellPage}" />
<Setter Property="Height" Value="{Binding ViewModel.CurrentMenuItemHeight, Mode=OneWay, ElementName=shellPage}" />
</Style>
</Page.Resources>
But it would be much better to use x:Bind instead of Binding. You can view this page to learn more about it https://learn.microsoft.com/en-US/windows/uwp/data-binding/data-binding-in-depth

UWP ResourceDictionary Style Error: The parameter is incorrect

I'm making a ResourceDictionary of common styles that are used throughout my application and one of them is:
<Style x:Key="ME_BASE_AppbarButtonSaveStyle"
TargetType="AppBarButton">
<Setter Property="Label"
Value="Save" />
<Setter Property="ToolTipService.ToolTip"
Value="Save" />
<Setter Property="Icon">
<Setter.Value>
<FontIcon FontFamily="Segoe MDL2 Assets"
Glyph="" />
</Setter.Value>
</Setter>
</Style>
It's all ok if I apply the style only one AppbarButton on the Page, but if I want to have two buttons with the same style, I get the following error:
The parameter is incorrect
It's of ok (no error) if I remove the icon property out of the style...
But that's kind of missing the point...
Anyone experienced something similar? Perhaps...
Thank you for all the help.
Error HRESULT E_Fail has been returned from a call to a COM component.
This error will occurred when you use this style for the second AppBarButton. This error usually happens when a reference to a style or an event handler that does not exist or is not with the context of the XAML, you can see the exception information of your problem:
If you read this document: XAML resources must be shareable, you will find:
Custom types used as resources can't have the UIElement class in their inheritance, because a UIElement can never be shareable (it's always intended to represent exactly one UI element that exists at one position in the object graph of your runtime app).
Whether a Icon property of AppBarButton or a FontIcon derives from UIElement, so I guess this is the reason why can't this property be styled in the resource dictionary.
Besides, I will consider if this is a right direction to define the Icon property for each AppBarButton in the style, normally I'd like give each button a different icon as content.
But if you insist to do this, I can provide you a workaround method by defining the Content of the AppBarButton, this is the construction of your AppBarButton:
You use a FontIcon as the content of the AppBarButton, so we can modify your style like this:
<Style x:Key="ME_BASE_AppbarButtonSaveStyle" TargetType="AppBarButton">
<Setter Property="Label" Value="Save" />
<Setter Property="ToolTipService.ToolTip" Value="Save" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<FontIcon FontFamily="Segoe MDL2 Assets"
Glyph="" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

What gives the hover and click styles in w8 xaml?

When using a listitem in a w8 app, how can I determine what gives the hover and click styles?
My listview looks like this:
<ListView x:Name="itemsListView"
TabIndex="1"
Visibility="Visible"
Padding="10,0,0,0" Foreground="Black"
ItemsSource="{Binding Nodes.Nodes}"
behaviors:ListViewItemClickedToAction.Action="{Binding SelectNodeAction}"
IsItemClickEnabled="True" FontFamily="Global User Interface"
>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When I hover using the mouse I get white letters and an almost white background.
I have tried reusing parts of the adventureworks shopper app, so there are styles from there copied. However, I can't understand what is applied to the ListView items.
You maybe already new this but if you check this screenshot you can see how you easily in VS2012 can create a copy of a built in style. When you press the "Edit a copy ..." a dialog will appear where you can choose where in the Project you want the style to be placed.
You can inherit styles. The inheritence of styles work in the following way:
<Style x:Name="BasicStyle" TargetType="Button">
<Setter Property="Background" Value="Green" />
</Style>
<Style x:Name="ButtonStyle" TargetType="Button" BasedOn="{StaticResource BasicStyle}">
<Setter Property="Foreground" Value="Red" />
</Style>
You can do inheritence in several steps so Another button style can inherit the "ButtonStyle".
You can thus make a style that only contains the Template property if you want to seperate it or reuse the behaviour and look of your style. But you cannot split the Visual State Manager into several styles since if you inherit a style which sets the template property and then if you want to change the Hover state of that style you need to make a copy of the whole template and only change that part in the code.
I Think this would be a nice improvement by MS if you could make a style which only contains the pressed state and then Another style which only contains the hover effect and so on.
I hope this answers your questions :) I would love to answer more questions regarding XAML if you have any!

How to disable ListView's Hover and Tile effects?

I want to disable Tile effect that is some kind of pushed effect and hover background color effect of ListView control, how can i do that?
Thanks
After some googling I found that the highlighting happens in the ListViewItemPresenter, which turns out to be pretty hidden. It's located inside the ControlTemplate of an ListViewItem, which is the ItemContainer for the ListView. The simplest way I've found to disable the effect is to simply override this ControlTemplate:
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<TextBlock Text="List Item" />
...
<TextBlock Text="List Item" />
source: https://blog.jonstodle.com/uwp-listview-without-highlighting-and-stuff/
Look at this question:
Disable cross-slide selection for a listview
You can also make changes to the template to remove any visual states and adornments - go to the designer and right click your ListView/Edit Additional Templates/Edit Generated Item Container (ItemContainerStyle)/Edit a Copy... - that will extract the template you can modify using your preferred method.

DXGrid change color of grid row DevExpress WPF

I have entity with property IsRemoved. When it is become true grid row should be Gray.
To do this I am using this code:
<dxg:TableView.RowStyle>
<Style TargetType="{x:Type dxg:GridRowContent}">
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsRemoved, Mode=OneWay}" Value="True">
<Setter Property="Background" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</dxg:TableView.RowStyle>
</dxg:TableView>
But It will run only when grid shows first time. I want to change color when value is changing. Property implement INotifyPropertyChange Event.
Note: this answer is legacy (see my other answer).
This answer is for DevExpress versions prior to v14.1, or DevExpress versions v14.1 and after with
UseLightweightTemplates="None".
You need to have an initial setter for the property you want to change. This is due to the order in which WPF uses styles.
Include this line after your style tag:
<Setter Property="Background" Value="Black" />
Full Example:
<dxg:TableView.RowStyle>
<Style TargetType="{x:Type dxg:GridRowContent}">
<Setter Property="Background" Value="Black" />
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsRemoved, Mode=OneWay}" Value="True">
<Setter Property="Background" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</dxg:TableView.RowStyle>
Starting with v14.1 of DevExpress, they introduced Optimized Mode which uses Lightweight Templates. This makes everything faster, but requires a change to how the styles and DataTriggers are specified.
Lightweight Templates are controlled by a the attached property UseLightweightTemplates="Row", which is on by default. It can be switched to None for backwards compatibility.
Here is a working MVVM example of how to color a row if the IsDirty property is set for any grid row.
<dxg:GridControl x:Name="MyGridControl"
ItemsSource ="{Binding MyViewModelList}"
SelectionMode="None"
VerticalAlignment="Stretch">
<dxg:GridControl.Resources>
<SolidColorBrush x:Key="GridRowIsDirty" Color="#FF602D2D" />
</dxg:GridControl.Resources>
<dxg:GridControl.View>
<dxg:TableView UseLightweightTemplates="Row" >
<dxg:TableView.RowStyle>
<Style TargetType="dxg:RowControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Row.IsDirty}" Value="True">
<Setter Property="Background" Value="{StaticResource GridRowIsDirty}" />
</DataTrigger>
</Style.Triggers>
</Style>
</dxg:TableView.RowStyle>
</dxg:TableView>
</dxg:GridControl.View>
<dxg:GridControl.Columns>
<dxg:GridColumn x:Name="Included" FieldName="Included"/>
<dxg:GridColumn x:Name="ColumnB" Header="Column B" FieldName="ColumnB" ReadOnly="True"/>
<dxg:GridColumn x:Name="ColumnC" Header="Column C" FieldName="ColumnC" ReadOnly="True"/>ReadOnly="True"/>
</dxg:GridControl.Columns>
</dxg:GridControl>
In the ViewModel behind this grid:
public ObservableCollection<MyViewModel> MyViewModelList { get; set; }
Every row in the grid points to a class of type MyViewModel, which contains a custom IsDirty flag which we can set on demand:
public bool IsDirty
{
get { return _isDirty; }
set
{
_isDirty = value;
OnPropertyChanged();
}
}
Appendix A: Additional Links
See DevExpress: How to disable focused/selected row colors.
See DevExpress: Optimized Mode.
See DevExpress: DXGrid: DataTrigger does not seem to work with UseLightweightTemplates="All".
See DevExpress: Binding to the RowData.Row property is not updated when changing a specific data row property.
See DevExpress: DxGrid: Grid does not update until I scroll the row on off and one the screen.
Appendix B: Other solutions
This also works most of the time, but it will not work if the source of the event is via a context menu, so it is not recommended:
<DataTrigger Binding="{Binding DataContext.IsDirty}" Value="True">
<Setter Property="Background" Value="{StaticResource GridRowIsDirty}" />
</DataTrigger>
Appendix C: AllowLiveDataShaping
If the trigger is not firing, try switching on AllowLiveDataShaping="True" in <GridControl>. However, try to avoid this as it (theoretically) has an impact on the speed of large, complex grids (it has no discernable impact on most grids of a reasonable size).
Appendix D: If all else fails, use a custom ControlTemplate
With the introduction of "UseLightweightTemplates", DevExpress has been focusing on speed. However, the techniques used for speed involve switching off bindings that might slow things down.
This means that if we change something in a DxGrid cell, the value in the ViewModel does not change until the user shifts to the next cell or row. This means that the ViewModel lags behind what is actually in the grid.
To fix this, the only solution that I could find was to bypass DevExpress's templates entirely, and use my own. This means that the DxGrid has no choice but to display a custom template which updates the ViewModel instantaneously as soon as the user edits it, which means that the row color changes immediately:
<dxg:GridControl Grid.Row="3" x:Name="TrsGridControl"
ItemsSource ="{Binding MyObservableCollection}"
VerticalAlignment="Stretch"
AllowLiveDataShaping ="True">
<dxg:GridControl.Resources>
<converter:TestConverter x:Key="TestConverter" />
<ControlTemplate x:Key="DisplayedOnTicketTrs">
<dxe:CheckEdit x:Name="DisplayedOnTicketCheckEdit" HorizontalAlignment="Center" IsChecked="{Binding RowData.Row.DisplayedOnTicket, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</ControlTemplate>
</dxg:GridControl.Resources>
<dxg:GridControl.View>
<dxg:TableView UseLightweightTemplates="All"/>
</dxg:GridControl.View>
<dxg:GridControl.Columns>
<dxg:GridColumn x:Name="DisplayedOnTicketTrs" DisplayTemplate="{StaticResource DisplayedOnTicketTrs}" Header="Displayed On Ticket?" HeaderToolTip="Displayed On Ticket?" AllowEditing="False"/>
Header ="Displayed On Ticket?"/>
<dxg:GridColumn x:Name="ColumnA" Header="ColumnA" FieldName="ColumnA" ReadOnly="True"/>
<dxg:GridColumn x:Name="ColumnB" Header="ColumnB" FieldName="ColumnB" ReadOnly="True"/>
</dxg:GridControl.Columns>
</dxg:GridControl>
After I made this change, everything started to work:
When the checkbox is clicked, the background color changes instantly (if we add the trigger to change the background color, above).
Editing the DxGrid changes the ViewModel instantaneously.
Changing the ViewModel updates the DxGrid instantaneously.
If a ContextMenu updates the ViewModel, then everything just works.
you should write just "Row" instead of "DataContext"