I've created a status bar:
<StatusBar>
<StatusBarItem >
<WrapPanel>
<Image Source="/MyApp;component/Images/icon.png" />
<TextBlock Name="_StatusbarUser" Text="Username" />
</WrapPanel>
</StatusBarItem>
</StatusBar>
How to create a trigger, when i set the textblock visibility to collapse,
it will also triger parent status bar visibility.
I've try using style bellow, but didn't work
<StatusBar Grid.Row="2" Name="_Statusbar">
<StatusBar.Resources>
<Style TargetType="{x:Type StatusBarItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type TextBlock}}, Path=Visibility}" Value="Collapsed">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBar.Resources>
<StatusBarItem>
<WrapPanel>
<Image Source="/MyApp;component/Images/icon.png" />
<TextBlock Name="_StatusbarUser" Text="Username" />
</WrapPanel>
</StatusBarItem>
</StatusBar>
Help please, thx
Better bind parent property to child property:
<StatusBar>
<StatusBarItem Visibility="{Binding ElementName=_StatusbarUser, Path=Visibility}">
<WrapPanel>
<Image Source="/MyApp;component/Images/icon.png" />
<TextBlock Name="_StatusbarUser" Text="Username" />
</WrapPanel>
</StatusBarItem>
</StatusBar>
Related
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>
I have a ListBox. This ListBox has custom controls. For example, image, textblocks, etc. Initially textblocks are collapsed, image is visible. I want to make the textblocks visible when user enter with mouse and change opacity of image in that item. But I can't access listboxitems controls. I named them but they don't show up.How can I achieve that ? My ListBox XAML codes:
<ListBox SelectionChanged="MoviesDisplay_OnSelectionChanged" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Background="Black" x:Name="MoviesDisplay">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="MoviesGrid" Height="355" Width="240" Margin="10,20,20,3" HorizontalAlignment="Left">
<Image x:Name="MoviePoster" Opacity="1" Source="{Binding Poster}"></Image>
<TextBlock Visibility="Collapsed" x:Name="MovieName" FontSize="18" Text="{Binding Name}" Foreground="White" Margin="0,15,0,321"></TextBlock>
<TextBlock Visibility="Collapsed" x:Name="MovieGenre" FontSize="16" Text="" Foreground="White" Margin="0,34,0,302"></TextBlock>
<TextBlock Visibility="Collapsed" x:Name="MovieReleaseDate" FontSize="16" Text="{Binding ReleaseDate}" Foreground="White" Margin="0,53,164,284"></TextBlock>
<materialDesign:RatingBar Visibility="Collapsed" Foreground="White"
Value="{Binding Rating}"
x:Name="MovieRatingBar" Margin="59,314,60,17"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
client.GetConfig();
var popularMovies = await client.GetMoviePopularListAsync("en", 1);
PopularMovies.Clear();
foreach (var movie in popularMovies.Results)
{
// ImgListUrls.Add(client.GetImageUrl("w500",movie.PosterPath).AbsoluteUri);
Movie mov = new Movie()
{
Id = movie.Id,
Name = movie.OriginalTitle,
ReleaseDate = movie.ReleaseDate.Value.ToShortDateString(),
Poster = client.GetImageUrl("w500", movie.PosterPath).AbsoluteUri,
Rating = movie.VoteAverage
};
PopularMovies.Add(mov);
}
MoviesDisplay.ItemsSource = PopularMovies;
// DownloadImages(ImgListUrls);
}
You don't need to access these elements in code behind. Add Styles with DataTriggers instead, e.g.:
<Image Source="{Binding Poster}">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding IsMouseOver,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<Setter Property="Opacity" Value="0.5"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
Put the TextBlocks in a StackPanel and add a similar Style:
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding IsMouseOver,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock FontSize="18" Text="{Binding Name}" Foreground="White"/>
<TextBlock FontSize="16" Text="{Binding ReleaseDate}" Foreground="White"/>
</StackPanel>
I need to show only the Icon in the combo box if the combo box is clicked the icon and the text needs to be shown. As I am not familiar with how to achieve this.
Please help me in achieving this.
<ComboBox Name="cmbColors">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16" Margin="0,2,5,2" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This will show the rectangle and text in the combo box and the drop down of the combo box. But my requirement is only to show the color filled rectangle in the combo box.
I am not sure whether I understand what you are trying to do, but my best guess is that you want to hide the text block if some other control has focus. If so, you can do something like this:
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16" Margin="0,2,5,2" />
<TextBlock x:Name="text" Text="{Binding Name}" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="False">
<Setter TargetName="text" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
If you are instead trying to say that you want a different template for the header than for the dropdown, then you could do something like described in one of the answers to this question.
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16" Margin="0,2,5,2" />
<TextBlock x:Name="text" Text="{Binding Name}" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ComboBoxItem}}}" Value="{x:Null}">
<Setter TargetName="text" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Okay so my problem is that I generate some elements on the fly, as linked to an array (for each element on the array there is another item in the XAML). I'm doing this with a TreeView control, albiet following an online example (which has been working well so far).
In this example, the groups are families and within each ... well, it's easier to show in an image here. Family is a class which contains an ObservableCollection of FamilyMembers, both of which are used to populate the TreeView.
Here's the main business:
<TreeView Name="trvFamilies">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type self:Family}" ItemsSource="{Binding Members}">
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ImageGroup}" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" [" Foreground="Blue" />
<TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
<TextBlock Text="]" Foreground="Blue" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type self:FamilyMember}">
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ImageUserMale}" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" (" Foreground="Green" />
<TextBlock Text="{Binding Age}" Foreground="Green" />
<TextBlock Text=" years)" Foreground="Green" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
This all works fine, but here's my problem: I have a male image for if a family member is male and a female one for if the family member is female. I don't know how to get at the elements to set the image based on this criterion. As you can see, at the moment the images are static.
Thanks!
You may use a DataTrigger, e.g. on a potential enum Gender property of your FamilyMember:
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{StaticResource ImageUserUnknownGender}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Gender}" Value="Male">
<Setter Property="Source" Value="{StaticResource ImageUserMale}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Gender}" Value="Female">
<Setter Property="Source" Value="{StaticResource ImageUserFemale}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
First of all, I will try to describe the context of what I need.
I have an application, which display a dashboard like this :
Dashboard-with-KPI
On each line of those KPIs, when there is a valueProcessed I want to see :
aTitle : valueToProcess/valueProcessed [%]
But when there isn't any valueProcessed I need this :
aTitle : 0 [n/a]
In order to achieve it, I used a DataTrigger like this :
<StackPanel Grid.Column="1" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding NbLocationToInstall}" Value="0">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding NbLocationInstalled}"/>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard_NoSpace}" Text="/" />
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding NbLocationToInstall}"/>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding PrLocationInstalled, StringFormat={}[{0:0.##} %]}" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding NbLocationToInstall}" Value="1">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding NbLocationToInstall}"/>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="[n/a]" />
</StackPanel>
</StackPanel>
Displaying a StackPanel depends on a binding value called NbLocationToInstall.
This solution works but, I have a lot of rows of KPI to display, and I don't want to repeat this block (=> n rows mean n block like this, just binding values will change).
So I have decided to create a dictionary, there I set my style :
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Dictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style x:Key="ValidTrigger" TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding NbLocationToInstall}" Value="0">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="InvalidTrigger" TargetType="StackPanel">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding NbLocationToInstall}" Value="1">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</UserControl.Resources>
And now, I can use it like this :
<StackPanel Grid.Column="1" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Style="{StaticResource ValidTrigger}">
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding NbLocationInstalled}"/>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard_NoSpace}" Text="/" />
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding NbLocationToInstall}"/>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding PrLocationInstalled, StringFormat={}[{0:0.##} %]}" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Style="{StaticResource InvalidTrigger}">
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding NbLocationToInstall}"/>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="[n/a]" />
</StackPanel>
</StackPanel>
But how can I change the binding value of DataTrigger style ValidTrigger ?
This is valid here for this example, given in the context, but for other rows, I want to pass another binding variable. And there, it is NbLocationToInstall, defined in ResourceDictionary.
Many thanks for any help.
According to the suggestion, I made a review of my code.
First of all, this is the XAML code definition :
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Dictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style x:Key="ValidTrigger" TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding ValueToProcess}" Value="0">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="InvalidTrigger" TargetType="StackPanel">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding ValueToProcess}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<!-- Detailed Information -->
<GroupBox Header="{x:Static lang:Resource.DetailedInformation}" Grid.Column="0" Grid.Row="0" Margin="5" Style="{StaticResource GroupBox_Default_Dashboard}">
<StackPanel>
<UniformGrid HorizontalAlignment="Stretch" Columns="2">
<TextBlock Style="{StaticResource TextBlock_Default}" HorizontalAlignment="left" Text="{x:Static lang:Resource.DeploymentTime}" />
<TextBlock Style="{StaticResource TextBlock_Default}" HorizontalAlignment="Right">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0:00} h {1:00;00} min">
<Binding Path="Time.Hours" />
<Binding Path="Time.Minutes" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</UniformGrid>
<ListBox ItemsSource="{Binding KPIs}" BorderBrush="Transparent" Background="Transparent" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="0.45" />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource TextBlock_Default}" HorizontalAlignment="Left" Text="{Binding Label}"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Style="{StaticResource ValidTrigger}">
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding ValueProcessed}"/>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard_NoSpace}" Text="/" />
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding ValueToProcess}"/>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding ValueProcessed, StringFormat={}[{0:0.##} %]}" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Style="{StaticResource InvalidTrigger}">
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="{Binding ValueToProcess}"/>
<TextBlock Style="{StaticResource TextBlock_Default_Dashboard}" Text="[n/a]" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</GroupBox>
This is the KPI model :
/// <summary>
/// provide a container to display a simple KPI
/// </summary>
public class KPI
{
// props
public string Label { get; private set; }
public double ValueToProcess { get; private set; }
public double ValueProcessed { get; private set; }
public double Percentage { get; private set; }
// default ctor
public KPI(string label, double valueToProcess, double valueProcessed)
{
this.Label = label;
this.ValueToProcess = valueToProcess;
this.ValueProcessed = valueProcessed;
this.Percentage = (valueToProcess == 0) ? Double.NaN
: ValueProcessed / ValueToProcess * 100;
}
}
This is the declaration :
ObservableCollection<KPI> _KPIs;
public ObservableCollection<KPI> KPIs
{
get { return _KPIs; }
set
{
_KPIs = value;
RaisePropertyChanged("KPIs");
}
}
This is the definition :
KPIs = new ObservableCollection<KPI>()
{
{ new KPI(Lang.Resource.Label1, 5, 0) },
{ new KPI(Lang.Resource.Label2, 0, 0) },
{ new KPI(Lang.Resource.Label3, 0, 0) }
};
And It works perfectly.
I hope it could help anyone else.