I have some RadioButton’s that I want to change the Visibility of based on multiple properties in the ViewModel. Each RadioButton’s Visibility will vary based on the same list of properties. I have the following xaml:
<RadioButton Command="{Binding Path=SomeCommand}"
CommandParameter="SomeCommandParameter"
Content="RB 1">
<RadioButton.Visibility>
<MultiBinding Converter="{StaticResource Rb1Visibility}">
<Binding Path="Value1"
RelativeSource="{RelativeSource Self}" />
<Binding Path="Value2"
RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</RadioButton.Visibility>
</RadioButton>
I would like to change the Converter for each RadioButton option, but everything else would remain the same (all the Bindings). I don’t want to duplicate all the xaml code for this. I originally tried creating a style for the RadioButton, but I could not figure out how to pass the Converter to the Style resource:
<Style x:Key="RbVisibilityStyle"
TargetType="{x:Type RadioButton}">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{???? Pass in converter ?????}">
<Binding Path="Value1"
RelativeSource="{RelativeSource Self}" />
<Binding Path="Value2"
RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
I could create a “Visibility” property for each RadioButton in the ViewModel, but that adds a lot of code to the ViewModel and addition PropertyChanged event handling.
Is there an easy way to generically bubble up the MultiBinding so I don’t duplicate the code throughout the entire xaml file? Is there a different approach I should be taking?
Update - Adding a more complex example
<RadioButton Command="{Binding Path=SomeCommand}"
CommandParameter="SomeCommandParameter"
Content="RB 1">
<RadioButton.Visibility>
<MultiBinding Converter="{StaticResource Rb1Visibility}">
<Binding Path="Value1"
RelativeSource="{RelativeSource Self}" />
<Binding Path="Value2"
RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</RadioButton.Visibility>
</RadioButton>
<RadioButton Command="{Binding Path=SomeCommand2}"
CommandParameter="SomeCommandParameter2"
Content="RB 2">
<RadioButton.Visibility>
<MultiBinding Converter="{StaticResource Rb2Visibility}">
<Binding Path="Value1"
RelativeSource="{RelativeSource Self}" />
<Binding Path="Value2"
RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</RadioButton.Visibility>
</RadioButton>
<RadioButton Command="{Binding Path=SomeCommand3}"
CommandParameter="SomeCommandParameter3"
Content="RB 3">
<RadioButton.Visibility>
<MultiBinding Converter="{StaticResource Rb3Visibility}">
<Binding Path="Value1"
RelativeSource="{RelativeSource Self}" />
<Binding Path="Value2"
RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</RadioButton.Visibility>
</RadioButton>
How do I reduce the MultiBinding redundancy here?
would like to change the Converter for each RadioButton option,
Create a new MultiValueConverter which takes an extra parameter (the radio button option) and then simply route the call to the appropriate converter based on that option.
One can put the option in the Tag property on the control's Xaml.
Example
Its not clear to me what that option you mention is, so in my example let us uniquely identify each Radio Button by its Tag property and use the routing converter to find the appropriate converter based off of the Tag supplied.
<RadioButton Tag="1"/><RadioButton Tag="2"/>
Then change the style to use the new converter with the new parameter:
<Style x:Key="RbVisibilityStyle"
TargetType="{x:Type RadioButton}">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource RouterViaTagVisibilityConverter">
<Binding Path="Value1"
RelativeSource="{RelativeSource Self}" />
<Binding Path="Value2"
RelativeSource="{RelativeSource Self}" />
<Binding Path="Tag"
RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
Related
I have a Border control inside a UserControl which is used as a switch for graphical changes. The Border has a context menu with two MenuItems: "Open" and "Close".
When the switch is closed only the "Open" MenuItem is visible, and when the switch is open only "Close" is visible. For some switches I need to completely disable Open or Close, so I don't want them to be visible at any time. Here is the code:
<Border.ContextMenu>
<ContextMenu Name="switchContextMenu">
<ContextMenu.Visibility>
<MultiBinding Converter="{StaticResource ContextMenuBoolToVisibility}">
<Binding Path="OpenAvailable" />
<Binding Path="OpenVisible" />
<Binding Path="CloseAvailable" />
<Binding Path="CloseVisible" />
</MultiBinding>
</ContextMenu.Visibility>
<MenuItem Name="miOpen" Header="{Binding Path=Resources.PowerControlSystem_OPEN, Source={StaticResource LocalizedStrings} }"
Click="miOpen_Click">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
<Binding Path="OpenAvailable" />
<Binding Path="OpenVisible" />
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
<MenuItem Name="miClose" Header="{Binding Path=Resources.PowerControlSystem_CLOSE, Source={StaticResource LocalizedStrings} }"
Click="miClose_Click">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
<Binding Path="CloseAvailable" />
<Binding Path="CloseVisible" />
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
</ContextMenu>
</Border.ContextMenu>
I use this switch as a generic component, so inside another .xaml code I set the "Open Available" and "Close Available" properties. Here is my problem:
I have a switch with the Open Available property set to False. By default, this switch is open. I can closed it, and that's OK. Then when I right click on this switch again the context menu is not visible. This is also OK.
The problem is when I change some values this switch opens again, and at that point of time, the Context Menu pops up. I think that the problem is in the Converter for the ContextMenu visibility. It is triggered at this point and the ContextMenu becomes visible.
Does anyone have an idea how to stop this behavior?
Instead of setting the ContextMenu visibility, you can style the Border
<Border>
<Border.Style>
<Style TargetType="Border">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Name="switchContextMenu">
<MenuItem Name="miOpen" Header="{Binding Path=Resources.PowerControlSystem_OPEN, Source={StaticResource LocalizedStrings} }"
Click="miOpen_Click">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
<Binding Path="OpenAvailable" />
<Binding Path="OpenVisible" />
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
<MenuItem Name="miClose" Header="{Binding Path=Resources.PowerControlSystem_CLOSE, Source={StaticResource LocalizedStrings} }"
Click="miClose_Click">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
<Binding Path="CloseAvailable" />
<Binding Path="CloseVisible" />
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Value="False">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ContextMenuBoolAggregate}">
<Binding Path="OpenAvailable" />
<Binding Path="OpenVisible" />
<Binding Path="CloseAvailable" />
<Binding Path="CloseVisible" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="ContextMenu" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
So the trigger is conditionally removing the ContextMenu instead of changing its visibility. I used the ContextMenuBoolAggregate converter name to indicate you need to create a new converter that is determining the correct value to remove the context menu - I think it's clear how to implement that part.
If your condition is simple enough, you can use MultiDataTrigger instead of DataTrigger with MultiBinding and Converter:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding OpenAvailable}" Value="False"/>
<Condition Binding="{Binding OpenVisible}" Value="False"/>
<Condition Binding="{Binding CloseAvailable}" Value="False"/>
<Condition Binding="{Binding CloseVisible}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="ContextMenu" Value="{x:Null}"/>
</MultiDataTrigger>
I found solution in another way. I used ContextMenuService.IsEnabled property. When is set to false, Context Menu can't be shown. So, I added this in my Border.xaml code:
<ContextMenuService.IsEnabled>
<MultiBinding Converter="{StaticResource ContextMenuBoolToVisibility}">
<Binding Path="OpenAvailable" ElementName="mySwitch" />
<Binding Path="OpenVisible" ElementName="mySwitch" />
<Binding Path="CloseAvailable" ElementName="mySwitch" />
<Binding Path="CloseVisible" ElementName="mySwitch" />
</MultiBinding>
</ContextMenuService.IsEnabled>
I also changed my ContextMenuBoolToVisibility converter. Instead of returning Visibility.Visible or Visibility.Collapsed, now returns true or false for ContextMenuService.IsEnabled property.
I´d like to reuse a MultiBinding and tried to use this solution, but I can´t seem to reach IsEnabled from a Setter Property.
So I tried this approach, but no cigar :
App.xaml
<Application x:Class="Test.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:Test.MODEL"
StartupUri="MainWindow.xaml">
<Application.Resources>
<view:BooleanConverter x:Key="BooleanConverter" />
<view:BooleanMultiConverter x:Key="BooleanMultiConverter" />
<MultiBinding x:Key="OnOffBinding" Converter="{StaticResource BooleanMultiConverter}" ConverterParameter="OR">
<Binding Path="model:CustomerIsDefined" Converter="{StaticResource BooleanConverter}" />
<Binding Path="model:CustomerIsConfirmed" Converter="{StaticResource BooleanConverter}" />
</MultiBinding>
</Application.Resources>
MainWindow.xaml > These two attempts will not compile :
<TextBox IsEnabled="{StaticResource OnOffBinding}"/>
<TextBox IsEnabled="{MultiBinding {StaticResource OnOffBinding}}" />
Any ideas?
EDIT: After accepting Funcs answer I guess this has allready been answered in the linked thread. Sorry..
Try wrapping it in Style
<Application.Resources>
<view:BooleanConverter x:Key="BooleanConverter" />
<view:BooleanMultiConverter x:Key="BooleanMultiConverter" />
<Style x:Key="ControlEnabler" TargetType="Control">
<Setter Property="IsEnabled">
<Setter.Value>
<MultiBinding x:Key="OnOffBinding" Converter="{StaticResource BooleanMultiConverter}" ConverterParameter="OR">
<Binding Path="model:CustomerIsDefined" Converter="{StaticResource BooleanConverter}" />
<Binding Path="model:CustomerIsConfirmed" Converter="{StaticResource BooleanConverter}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
MainWindow.xaml
<TextBox Style="{StaticResource ControlEnabler}"/>
Note TargetType="Control" making it generic.
<MultiBinding Converter="{StaticResource MergeValuesConverter}">
<Binding Path="StructureID" />
<Binding Path="SectionID" />
<Binding Path="ParentStructureID" />
</MultiBinding>
I am only getting the first two bindings.
Is there a way to get the third binding (ParentStructureID)?
I need to bind a checkBox to TWO property and I think that i have to use multiBindings
so far i have this, but this doesn't work.
<CheckBox x:Name="FilterAll" Content="All">
<CheckBox.IsChecked>
<MultiBinding>
<Binding Path="SearchEngineCompassLogView.FilterSearch.IsFilterAllEnable"
Source="{StaticResource CompassLogView}">
</Binding>
<Binding Path="SearchEngineCompassLogView.FilterSearch.IsFilterVisible"
Source="{StaticResource CoreServiceLogView}">
</Binding>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
is this even possible with MultiBinding?
You could use MultiBinding. And as ethicallogics said, you must use a converter to do the actual logic of the parameters (whether you want to do AND, OR, whatever. You can see a little more about those here
I'm not sure what you are trying to affect on your checkbox, but in the end it will look something like this.
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource MultiBoolConverter}">
<Binding Path="SearchEngineCompassLogView.FilterSearch.IsFilterAllEnable" Source="{StaticResource CompassLogView}"/>
<Binding Path="SearchEngineCompassLogView.FilterSearch.IsFilterVisible"
Source="{StaticResource CoreServiceLogView}"/>
</MultiBinding>
</CheckBox.IsChecked>
There is also another way to do this as well, which I sometimes find useful. It's called DataTriggers. If you've done any work with Styles and Templates then you may have seen them before. Here is an example based on your question:
<CheckBox>
<CheckBox.Style>
<Style TargetType={x:Type CheckBox}>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path="SearchEngineCompassLogView.FilterSearch.IsFilterAllEnable" Source="{StaticResource CompassLogView}" Value="True"/>
<Condition Binding="{Binding Path="SearchEngineCompassLogView.FilterSearch.IsFilterVisible" Source="{StaticResource CoreServiceLogView}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="CheckBox.IsChecked" Value="True"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
You must specify converter in MultiBinding.Multibinding
I'm using dataTemplate. This is the template:
<ItemsControl ItemsSource="{Binding RAM.Partitions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Position, StringFormat={}{0}k}"/>
<Grid Grid.Column="1">
<Border>
<Border.Height>
<MultiBinding Converter="{StaticResource MultiplyConverter}">
<Binding ElementName="LayoutRoot" Path="ActualHeight"/>
<Binding Path="Size" />
<Binding Path="RAM.Size" />
</MultiBinding>
</Border.Height>
</Border>
</Grid>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Can you see this line?
<Binding Path="RAM.Size" />
That line throws me an exception, it should be because RAM.Size is from a parent element. How might I get that value?
Thanks in advance!
So you're trying to get to the RAM.Size value on the same object that your ItemsControl is getting its ItemsSource from?
See if this works:
<MultiBinding Converter="{StaticResource MultiplyConverter}">
<Binding ElementName="LayoutRoot" Path="ActualHeight"/>
<Binding Path="Size" />
<Binding Path="DataContext.RAM.Size"
RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}" />
</MultiBinding>
So the binding is going up in through the visual tree to the ItemsControl, then binding to the Ram.Size property of its DataContext.