I have the following xaml markup:
<GroupBox Margin="10" Padding="20" Header="Case 5 - Custom Error Object">
<GroupBox.DataContext>
<local:ViewModel4/>
</GroupBox.DataContext>
<StackPanel>
<Label Name="AdornerElement" Style="{StaticResource ResourceKey=AdornerElementStyle}"/>
<TextBox Text="{Binding Path=UserName, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
Margin="0 10"
Validation.ValidationAdornerSite="{Binding ElementName=AdornerElement}">
</TextBox>
</StackPanel>
</GroupBox>
and the following style:
<Style x:Key="AdornerElementStyle" TargetType="Label">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<StackPanel Orientation="Horizontal" Background="LightCoral">
<Image Source="/clipart.png" Width="24" Margin="10"/>
<ItemsControl ItemsSource="{Binding ElementName=AdornerElement, Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)}"
VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ErrorContent.ValidationMessage}"
Style="{StaticResource CustomErrorTypeStyle}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=AdornerElement, Path=Validation.HasError}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Everything works fine except Trigger.
If I set up the property "Visibility" to "Visible" initially, then I can see that error messages are shown correctly.
If I use style shown before, then the Label remains to be collapsed.
Please, help me to use triggers correctly to achieve the final result.
This is not how Validation.ValidationAdornerSite works. This attached property just defines the element that will be adorned with the validation error template. By default this is the element that is validating, the Binding.Target to be more precise.
When you set Validation.ValidationAdornerSite the binding engine will automatically set the attached Validation.ValidationAdornerSiteFor property to reference the element that Validation.ValidationAdornerSite was originally set on.
This means that Validation.HasError and other related attached properties are always set on the Binding.Target and not on the adorner site.
That's why your triggers don't work: they are triggering on the Label instead of the TextBox (where the validation/binding error is registered).
To fix it, the DataTrigger must get the Validation.HasErrors attached property value of the Validation.ValidationAdornerSiteFor attached property, which references the original Binding.Target (the TextBox).
<Style x:Key="AdornerElementStyle"
TargetType="Label">
<Setter Property="Visibility"
Value="Collapsed" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<StackPanel Orientation="Horizontal"
Background="LightCoral">
<Image Source="/clipart.png"
Width="24"
Margin="10" />
<ItemsControl ItemsSource="{Binding ElementName=AdornerElement, Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)}"
VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent.ValidationMessage}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=AdornerElement, Path=(Validation.ValidationAdornerSiteFor).(Validation.HasError)}"
Value="True">
<Setter Property="Visibility"
Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Remarks
It's not clear why you are using a Label to display the error messages. You could simply define an error template, which is a ControlTemplate that is referenced by the attached Validation.ErrorTemplate property. This would simplify your code significantly.
The following example creates the exact same visual error feedback but without the hassle for an additional Label and associated triggers to manage the visibility of the error messages. All this is all handled by the WPF binding engine.
<Window>
<Window.Resources>
<!-- The Validation.Errors property is the DataContext of this template -->
<ControlTemplate x:Key="ErrorTemplate">
<StackPanel>
<Border BorderBrush="Red"
BorderThickness="1"
Background="LightCoral">
<StackPanel Orientation="Horizontal">
<Image Source="/clipart.png"
Width="24"
Margin="10" />
<ItemsControl ItemsSource="{Binding}"
VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent.ValidationMessage}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
<Border BorderBrush="Transparent"
BorderThickness="1"
HorizontalAlignment="Left">
<!-- Placeholder for the Binding.Target -->
<AdornedElementPlaceholder x:Name="AdornedElement" />
</Border>
</StackPanel>
</ControlTemplate>
</Window.Resource>
<StackPanel>
<TextBox Text="{Binding Path=UserName, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
Validation.ErrorTemplate="{StaticResoucre ErrorTemplate}" />
</StackPanel>
</Window>
Related
I have a list box defined as below in xaml:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Visibility="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}}" />
<ContentPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="tb_Text" Text="{Binding IsSelected}"> </TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
I want to bind some property of tb_Text (say text property) to some property of the datacontext of the listbox (IsSelected property in this example). Is there a way to achieve that?
Note: IsSelected property is coming from the templated parent which has this listbox defined in it's Template.
Using ElementName and binding to the Visibility property of the Ellipse does the trick.
Is it possible to set the same style to multiple controls?
I was tried the following way. But 1st Button style was not applied correctly, in second style applied fine.
Design:
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="White" Margin="0,0,5,0">1st Button</TextBlock>
<Button Style="{StaticResource ViewButton}" />
<TextBlock Foreground="White" Margin="25,0,5,0">2nd Button</TextBlock>
<Button Style="{StaticResource ViewButton}" />
</StackPanel>
Resource:
<Style x:Key="ViewButton" TargetType="Button" BasedOn="{StaticResource ButtonStyle}">
<Setter Property="Content">
<Setter.Value>
<StackPanel Orientation="Horizontal">
<Image Source="/Images/View.png" Stretch="None" Width="24" Height="24" />
<TextBlock Margin="5,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold">View</TextBlock>
</StackPanel>
</Setter.Value>
</Setter>
<Setter Property="Padding" Value="2,0,10,0"/>
</Style>
You're setting twice the same Content to two different Controls. The problem is that the StackPanel in the Setter.Value can't have two Parents so the last use will be applied. You can use ContentTemplate to make it work:
<Style x:Key="ViewButton" TargetType="Button" BasedOn="{StaticResource ButtonStyle}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="/Images/View.png" Stretch="None" Width="24" Height="24" />
<TextBlock Margin="5,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold">View</TextBlock>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Padding" Value="2,0,10,0"/>
</Style>
....
....
....
You have to mention the TargetType in the as Button. and You need to write the style property following the style name as i wrote.
Hope this will work for You..
thanks
The design image is
This codes is my wrote,but the background is larger then my expection,i hope the data change when i click one category,the selected label highlight
my codes is
<DataTemplate x:Key="varietyListViewTemplate" x:DataType="local:Category" >
<TextBlock Text="{x:Bind title}" FontSize="12" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center" />
</DataTemplate>
<Style x:Key="varietyListViewStyle" TargetType="ListView">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="varietyListViewItemStyle" TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter
Foreground="#979797"
Background="#ffffff"
SelectedForeground="#ffffff"
SelectedBackground="#fd8c00"
PressedBackground="#fd8c00"
SelectedPressedBackground="#fd8c00"
SelectedPointerOverBackground="#fd8c00"
Margin="2"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- PointerOverBackground="#fd8c00"
SelectedForeground="#ffffff"
PointerOverForeground="#ffffff" -->
</Page.Resources>
<Grid>
<Grid Background="#FFFFFF">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListView x:Name="varietyListView" Grid.Row="0"
ItemsSource="{x:Bind Path=Categorys}" ItemTemplate="{StaticResource varietyListViewTemplate}"
IsItemClickEnabled="True" SelectionMode="Single" Style="{StaticResource varietyListViewStyle}" ItemContainerStyle="{StaticResource varietyListViewItemStyle}"
ItemClick="varietyListView_ItemClick" >
</ListView>
<ListView x:Name="varietyListView2" Grid.Row="0"
ItemsSource="{x:Bind Path=Categorys}" ItemTemplate="{StaticResource varietyListViewTemplate}"
IsItemClickEnabled="True" SelectionMode="Single" Style="{StaticResource varietyListViewStyle}" ItemContainerStyle="{StaticResource varietyListViewItemStyle}"
ItemClick="varietyListView2_ItemClick" Margin="0,0,0,50">
</ListView>
<Frame x:Name="varietyFrame" Grid.Row="1"/>
</Grid>
<Grid Name="listImage">
</Grid>
</Grid>
That is because the ListViewItem’s size is larger than actual size of the text. By checking the default template of ListViewItem, you will find the MinWidth and MinHeight properties are specified as following.
<Setter Property="MinWidth" Value="{ThemeResource ListViewItemMinWidth}"/>
<Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}"/>
<x:Double x:Key="ListViewItemMinWidth">88</x:Double>
<x:Double x:Key="ListViewItemMinHeight">44</x:Double>
To find the default template, checking ListViewItem styles and templates or in generic.xaml (typical in C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10240.0\Generic) by searching "Default style for Windows.UI.Xaml.Controls.ListViewItem".
To work around this issue, we can try to make the ListViewItem has same size as the actual text by setting ListViewItem's MinWidth and MinHeight to "0" and use Margin to control the spacing between them. So apply the following code should get what you want:
<Style x:Key="varietyListViewItemStyle" TargetType="ListViewItem">
<Setter Property="MinWidth" Value="0" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter
Foreground="#979797"
Background="#ffffff"
SelectedForeground="#ffffff"
SelectedBackground="#fd8c00"
PressedBackground="#fd8c00"
SelectedPressedBackground="#fd8c00"
SelectedPointerOverBackground="#fd8c00"
Margin="20"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I recommend that you nest Grid to ListviewItem, and then you can change the backgroud of the listitems or just the background of the Grid, and of course you can shape the size of this Grid.
For example:
<ListView x:Name="varietyListView" Grid.Row="0" >
<ListView.ItemTemplate >
<DataTemplate>
<Grid>
<TextBlock Text="{Binding ...}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have a dynamically generated DataGrid bound to a DataTable property in my ViewModel.
I have AutoGenerateColumnHeaders = true, and it's working fine. However, I'm using a DataTemplate to cover the Header with a StackPanel containing a Label and Button. I cannot seem to figure out how to bind the Label Content to the DataGridColumnHeader. I have tried with and without FindAncestor, but I believe the following is the closest to where I need to be...Question is on the Label Content="{}"
<local:UserControlViewBase.Resources>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Black" BorderThickness="1">
<StackPanel Width="Auto" Orientation="Horizontal">
<Label Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type local:UserControlViewBase}},Path=DataContext.TestList.ColumnName}" Padding="12,0,12,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Content="Ok" Padding="12,0,12,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:UserControlViewBase.Resources>
//local:UserControlViewBase is just a UserControl with some extra bells and whistles added.
I'm fairly new to WPF and I'm assuming I'm just missing something with the binding - I'm still learning.
Thanks.
This is what I did to get it to work. I had to change the findancestor to look for the DataGridColumnHeader instead of the user control. I then was able to access the Column.Header property:
<local:UserControlViewBase.Resources>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Black" BorderThickness="1">
<StackPanel Width="Auto" Orientation="Horizontal">
<Label Width="75" Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridColumnHeader}},Path=Column.Header}" Padding="12,0,12,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Content="Ok" Padding="12,0,12,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:UserControlViewBase.Resources>
I have a ListBox...
<ListBox Margin="10" ItemsSource="{Binding Employees}"
ItemTemplate="{DynamicResource EmployeesTemplate}"
HorizontalContentAlignment="Stretch" BorderThickness="0"
ScrollViewer.CanContentScroll="False"/>
...that has a custom DataTemplate:
<Window.Resources>
<DataTemplate x:Key="EmployeesTemplate">
<Border BorderThickness="1" BorderBrush="Black" SnapsToDevicePixels="True"
DockPanel.Dock="Top" Margin="0,0,0,5" Padding="5">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}" Padding="0,0,5,0"/>
<TextBlock Text="{Binding Path=LastName}"/>
</StackPanel>
<TextBlock Grid.Column="1" Text="{Binding Path=Title}"/>
</Grid>
<StackPanel>
<TextBlock Text="{Binding Path=DateOfBirth, StringFormat={}{0:MM/dd/yyyy}}"/>
<TextBlock Text="{Binding Path=Address}"/>
<TextBlock Text="{Binding Path=PhoneNumber}"/>
<TextBlock Text="{Binding Path=Salary, StringFormat={}{0:C}}"/>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
When I select an item from the ListBox, the selection highlighting includes the 5-point Margin that is assigned to the bottom of each item (Border in DataTemplate):
You'll notice the similar situation on the left side, where the highlight overflows just a little bit. I did not notice that until now... hmm. So, I would like to restrict the selection highlight to the border area and nothing outside of it and be able to retain the margin spacing between the items.
How would I accomplish that? I tried manipulating Padding and Margin as much as I could, but I could not figure it out. Maybe I have to create a custom ListBox template?
I'm not sure if there is a simpler way but it works for me. I added a DataTemplate.Triggers section to my DataTemplate...
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}, Path=IsMouseOver}" Value="True">
<Setter Property="Background" Value="LightBlue" TargetName="EmployeesTemplateBorder"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="True">
<Setter Property="Background" Value="RoyalBlue" TargetName="EmployeesTemplateBorder"/>
</DataTrigger>
</DataTemplate.Triggers>
...and a ListBox.Resrouces section to the ListBox (to get rid of the default highlight behavior):
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
</ListBox.Resources>
Obviously, that doesn't do anything to the foreground, but that's not the concern right now, since that one is relatively easy comparing to this highlight routine.