I want to enable/disable a ComboBox based on if there is an item selected in another ComboBox. I was able to get it working by setting a trigger on the Style, but that overrides my custom global style for the combobox. Is there another way to get the same functionality without losing my style?
<ComboBox Grid.Column="1" Grid.Row="1"
Name="AnalysisComboBox"
MinWidth="200"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Path=AvailableAnalysis}">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem,ElementName=ApplicationComboBox}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
You don't need to do this via a Style, you can bind the IsEnabled property directly using a value converter as follows:
<ComboBox Grid.Column="1" Grid.Row="1"
Name="AnalysisComboBox"
MinWidth="200"
VerticalAlignment="Center" HorizontalAlignment="Left"
IsEnabled={Binding SelectedItem, ElementName=ApplicationComboBox, Converter={StaticResource NullToFalseConverter}}"
ItemsSource="{Binding Path=AvailableAnalysis}"/>
Where NullToFalseConverter is a key to an instance of the followsing converter:
public class NullToFalseConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value == null;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Yes, you can set BasedOn attribute to "inherit" your global style:
<ComboBox Grid.Column="1" Grid.Row="1"
Name="AnalysisComboBox"
MinWidth="200"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Path=AvailableAnalysis}">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}"
BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem,ElementName=ApplicationComboBox}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
Instead of {StaticResource {x:Type ComboBox}} you can set the key of you global style (if it is not implicit).
But for this particular task you don't need to define a style. You can just set a binding to IsEnabled property and use a converter to convert selected item of another combo box to a boolean:
<ComboBox Grid.Column="1" Grid.Row="1"
Name="AnalysisComboBox"
MinWidth="200"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Path=AvailableAnalysis}"
IsEnabled="{Binding SelectedItem,ElementName=ApplicationComboBox, Converter={StaticResource NotNullConverter}"/>
You could simply have a "normal" binding, with a value converter for changing "value exists" => true, "value is null" => false.
Related
I have a problem with a binding, I want to change an icon when the column has at least one element filtered. But it is not working.
This is my converter:
public class FilteredToIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int index = ((DataGridColumnHeader)((MahApps.Metro.IconPacks.PackIconMaterial)value).TemplatedParent).Column.DisplayIndex;
return !((AdvancedDataGrid)parameter).FilterLists[index].Any(item => item.NotFiltered);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
A filter for each column is generated with a contentTemplate, and I have 2 problems, If I set this style in controlTemplate.Resources the dataGrid reference is not found.
If I set the style in the DataGrid.Resources then I get
InvalidCastException: Can not convert an object from type 'System.Windows.Controls.DataGrid' to type 'MahApps.Metro.IconPacks.PackIconMaterial'.
This is the style:
<Style TargetType="iconPacks:PackIconMaterial">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource FilteredToIcon}, ConverterParameter={x:Reference dataGrid}}" Value="True">
<Setter Property="Kind" Value="FilterMenu"/>
</DataTrigger>
</Style.Triggers>
</Style>
And this is a summary of the whole XAML of my custom AdvancedDataGrid:
<DataGrid x:Class="ZOT.GUI.Items.AdvancedDataGrid"
x:Name="dataGrid"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:local="clr-namespace:ZOT.GUI.Items">
<DataGrid.Resources>
<local:FilteredToIconConverter x:Key="FilteredToIcon" />
<!-- The style can be here-->
</DataGrid.Resources>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<ControlTemplate.Resources>
<!-- The style can be here as well, whatever that works-->
</ControlTemplate.Resources>
<Grid>
<ContentPresenter/>
<ToggleButton x:Name="Y">
<!-- This is the Icon I want to change -->
<iconPacks:PackIconMaterial Kind="Filter"/>
</ToggleButton>
<Popup x:Name="pop" Width="auto" IsOpen="{Binding IsChecked, ElementName=Y,Mode=TwoWay}">
<!-- .... -->
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
If you have any clue, tell me please.
Thank you.
If you want to bind to the PackIconMaterial itself, you should set the RelativeSource property of the binding to RelativeSource.Self:
<Style TargetType="iconPacks:PackIconMaterial">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource FilteredToIcon}, ConverterParameter={x:Reference dataGrid},
RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Kind" Value="FilterMenu"/>
</DataTrigger>
</Style.Triggers>
</Style>
That's the only way you will be able to cast value to MahApps.Metro.IconPacks.PackIconMaterial in the converter.
There are probably better ways of solving whatever you're trying to do though.
I have the need to write part of the text inside a button with a different foreground. Until now, following this solution, i was using the following piece of code:
<Button>
<TextBlock Margin="20"
FontSize="24"
FontStyle="Normal"
FontWeight="Medium"
Foreground="#FF1384F5">
<Run>text1</Run>
<Run Foreground="#FF1372D3" Text="{Binding MyBinding}"/>
<Run >text2</Run>
</TextBlock>
</Button>
now i have the need to change the whole text based on a trigger, so i builded a DataTriger like the following:
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding MyBoolean}" Value="True">
<Setter Property="Text">
<Setter.Value>
<Run>text1</Run>
<Run Foreground="#FF1372D3" Text="{Binding MyBinding}"/>
<Run >text2</Run>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding MyBoolean}" Value="False">
<Setter Property="Text" Value="text3" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
of course this doesn't work because the property value is setted more than once. Looking for a solution i found this . It basically says to use multi binding
<Setter.Value>
<MultiBinding StringFormat="{}{0}{1}{2}"><!-- Format as you wish -->
<Binding Path="SelectedItem.iso"/>
<Binding Source="{x:Static System:Environment.NewLine}"/>
<Binding Path="SelectedItem.value"/>
</MultiBinding>
</Setter.Value>
But i'm not sure if it fit my situation, and how to use it eventually.
Runs will be set to the TextBlock.Inlines property, which has only getter, no setter. So you can't set Runs in the Style.
You can use two TextBlock elements and bind MyBoolean to the Visibility property of them:
<Grid>
<Grid.Resources>
<local:BoolToVisConverter x:Key="btoviscnv"/>
</Grid.Resources>
<TextBlock Text="text3" Visibility="{Binding MyBoolean, Converter={StaticResource btoviscnv}, ConverterParameter='not'}"/>
<TextBlock Visibility="{Binding MyBoolean, Converter={StaticResource btoviscnv}}">
<Run>text1</Run>
<Run Foreground="#FF1372D3" Text="{Binding MyBinding}"/>
<Run>text2</Run>
</TextBlock>
</Grid>
public class BoolToVisConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var bvalue = (bool)value;
if ((parameter as string)?.Equals("not") ?? false)
{
bvalue = !bvalue;
}
return bvalue ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException("It's one way converter");
}
}
I am currently making a grid that in each cell there is a label, the content in the labels are coming from a list using DataBinding.
I am trying to change the color of each cell depending on the value in the label. Eg if value = 1 then the background must be black.
This is the code i have now:
<Window.Resources>
<DataTemplate x:Key="DataTemplate_Level2">
<Label Content="{Binding}" Width="70" Height="70" HorizontalContentAlignment="Center">
</Label>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_Level1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
I tried different ways using triggers but nothing seemed to Work.
Any help would be appreciated.
Thanks
It's as easy as this, but you'll be copying and pasting a lot of Setter tags. You might want to consider a value converter instead (see below).
<DataTemplate x:Key="DataTemplate_Level2">
<Grid
SnapsToDevicePixels="True"
x:Name="Background">
<Label Content="{Binding}" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="1">
<Setter TargetName="Background" Property="Background" Value="Black" />
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="2">
<Setter TargetName="Background" Property="Background" Value="Khaki" />
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="3">
<Setter TargetName="Background" Property="Background" Value="YellowGreen" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
And here's the value converter version:
<Window.Resources>
<local:ColorConverter x:Key="ColorConverter" />
<DataTemplate x:Key="DataTemplate_Level2">
<Grid
SnapsToDevicePixels="True"
Background="{Binding Converter={StaticResource ColorConverter}}">
<Label Content="{Binding}" />
</Grid>
</DataTemplate>
</Window.Resources>
C#
public class ColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Color clr = Colors.SteelBlue;
var s = value as String;
// Add code here to pick a color or generate RGB values for one
switch (s) {
case "1":
clr = Colors.Black;
break;
}
return new SolidColorBrush(clr);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I have a combobox that has the period of the day as its items. I am trying to make the isSelected item based on the period on the day through the use of a converter but for some reason my code won't work.
Below is my xaml:
<Window.Resources>
<staticData:SelectedPeriodConverter x:Key="SelectedPeriodConverter"/>
</Window.Resources>
<ComboBox Grid.Column="4" Margin="0,7" Width="100" HorizontalAlignment="Right" Name="PeriodPicker" VerticalAlignment="Top" Height="25" SelectedItem="PM">
<ComboBoxItem>AM</ComboBoxItem>
<ComboBoxItem>PM</ComboBoxItem>
<ComboBox.ItemContainerStyle>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, RelativeSource={RelativeSource Self}, Converter={StaticResource SelectedPeriodConverter}}" Value="True">
<Setter Property="ComboBoxItem.IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
and my c# code for the converter is as follows:
public class SelectedPeriodConverter : IValueConverter
{
public object Convert(object values, Type targetType, object parameter, CultureInfo culture)
{
string test = values.ToString();
if (test == DateTime.Now.ToString("tt"))
{
return true;
}
else
{
return false;
}
}
public object ConvertBack(object value, Type targetTypes, object parameter, CultureInfo culture)
{
return value;
}
}
The weird thing is is if I change isSelected to IsEnable it will trigger but otherwise it won't.
Another way I tried was to have the styling in the Windows resources.
This worked if I targetted comboboxitem but would not work if I added an x:class and used ItemContainerStyle to isolate the trigger to one combobox as I did not want it working on all the comboboxes I had in my form.
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, RelativeSource={RelativeSource Self}, Converter={StaticResource SelectedPeriodConverter}}" Value="True">
<Setter Property="IsSelected" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
<ComboBox Grid.Column="4" Margin="0,7" Width="100" HorizontalAlignment="Right" Name="PeriodPicker" VerticalAlignment="Top" Height="25" SelectedItem="PM">
<ComboBoxItem>AM</ComboBoxItem>
<ComboBoxItem>PM</ComboBoxItem>
</ComboBox>
Does anyone know how I can get this to work?
thanks Callum
The fix was quiet simple.
Inserting the second code inside ComboBox.Resources tags instead of Windows.Resources tags worked for me like so:
<ComboBox.Resources>
<staticData:SelectedPeriodConverter x:Key="SelectedPeriodConverter"/>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, RelativeSource={RelativeSource Self}, Converter={StaticResource SelectedPeriodConverter}}" Value="True">
<Setter Property="IsSelected" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Resources>
thanks Callum
I have the following code (only relevant snippets)
<ribbon:RibbonWindow .............>
<Grid>
<ribbon:Ribbon>
<ribbon:RibbonToggleButton
x:Name="Button2"
SmallImageSource="Images\SmallIcon.png"
Label="Properties">
<ribbon:RibbonToggleButton.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter TargetName="SPanel1" Property="UIElement.IsVisible" Value="False"/>
<Setter TargetName="SPanel2" Property="UIElement.IsVisible" Value="True"/>
</Trigger>
</ribbon:RibbonToggleButton.Triggers>
</ribbon:RibbonToggleButton>
</ribbon:Ribbon>
</Grid>
</ribbon:RibbonWindow>
And when i run the application, it hangs up and te debugger pops up. What am i doing wrong?
I have tried setting the trigger on the grid, wrapping everything in a control template and setting a trigger there, same problem!
Edit
I have tried creating a button, and only the tag of trigger causes the Unhandled Exception error.
<Button Content="Button" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="12,27,0,0" Name="TestButton" VerticalAlignment="Top" Width="75" >
<Button.Triggers>
<Trigger></Trigger>
</Button.Triggers>
</Button>
Edit 2
Using Data triggers makes the Unhandled Exception go away but the trigger is not responding:
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Button2, Path=ToggleButton.IsChecked}" Value="True">
<Setter Property="UIElement.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
Edit 3
<ribbon:RibbonToggleButton
x:Name="Button2"
SmallImageSource="Images\SmallIcon.png"
Label="Properties">
</ribbon:RibbonToggleButton>
And The panel to be hidden
<StackPanel
Grid.Row="2"
Grid.Column="1"
x:Name="SPanel1"
Visibility="Visible">
<Label>*Deafult Grid*</Label>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Button2, Path=ToggleButton.IsChecked}" Value="True">
<Setter Property="UIElement.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
And for second StackPanel that i want to show
<StackPanel
Grid.Row="2"
Grid.Column="1"
x:Name="SPanel2"
Visibility="Hidden">
<Label>*Panel 2 *</Label>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Button2, Path=IsChecked}" Value="True">
<Setter Property="UIElement.Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
You need to use DataTrigger, and set the style property of the two panels to the IsChecked propery of the checkbox, here an example
<CheckBox Name="check" Content="Prova" IsChecked="True"> </CheckBox>
<Canvas Name="SPanel1" Background="Blue" Width="100" Height="100">
<Canvas.Style>
<Style TargetType="Canvas">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=check, Path=IsChecked}" Value="True">
<Setter Property="Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Style>
</Canvas>
UIElement.IsVisible is not a mutable property. You'll have to set UIElement.Visibility, which is an enum.
The reason why you get an exception is because FrameworkElement.Triggers only accepts EventTrigger. This means that if you do UIElement.Triggers (Button.Triggers or ribbon:RibbonToggleButton.Triggers), you can only add EventTriggers under that. Style, DataTemplate and ControlTemplate accept all TriggerBase derived classes.
Edit
As Mackho pointed out, you cannot use TargetName under Style. You'd have to accomplish this using data binding.
Assuming SPanel1 is a StackPanel, here's what you can do:
Create a converter
public class BoolToVisibilityConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Somewhere in the root control's resources, add BoolToVisibilityConverter
Add x:Name to your toggle button; lets say you call it "MyToggleButton"
<StackPanel x:Name="SPanel1" ...
Visibility="{Binding IsChecked, ElementName=MyToggleButton, Converter={StaticResource BoolToVisibilityConverter}}" />
And do something very similar for SPanel2.