How to access a MultiBinding defined in Application Resources - c#

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.

Related

WPF Context Menu pops up after Binding changes

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.

Per-item converter instance in style of treeview item container

I have ItemContainerStyle set for TreeView and use MultiBinding with converter in it:
<TreeView>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected">
<Setter.Value>
<MultiBinding Converter="{StaticResource SelectedCategoryConverter}" Mode="TwoWay">
<Binding Path="."/>
<Binding Path="CurrentCategoryId" RelativeSource="{RelativeSource AncestorType=UserControl}"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
I need SelectedCategoryConverter to be created for every unique item in the tree view, so I declared it with x:Shared="False" in window resources:
<local:SelectedCategoryConverter x:Shared="false" x:Key="SelectedCategoryConverter"/>
but it doesn't help: only one instance of converter is created when 2 or more items passed to TreeView through ItemsSource. I tried to write converter as MarkupExtension, but it didn't help too.
I think there is only one way.
1. Move ItemContainerStyle to the resource and marked it as nonshared:
<Application.Resources>
<Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}" x:Shared="False">
<Setter Property="IsSelected">
<Setter.Value>
<MultiBinding Converter="{local:SelectedCategoryConverterCreator}" Mode="TwoWay">
<Binding Path="."/>
<Binding Path="CurrentCategoryId" RelativeSource="{RelativeSource AncestorType=UserControl}"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="IsExpanded" Value="True" />
</Style>
</Application.Resources>
<TreeView ItemContainerStyle="{StaticResource TreeViewItemStyle}">
Create your own MarkupExtension which will create a new instance of SelectedCategoryConverter:
public class SelectedCategoryConverterCreatorExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new SelectedCategoryConverter();
}
}
It will create a new instance of SelectedCategoryConverter for each item. But remember that it is not very memory efficient.

Generically replace Converter in MultiBinding node

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>

WPF Databinding to Window

I want to link the DataBinding of a listbox item with a new window
A "Name" button should open a new window with the DataBinding of the listbox item.
The Binding is a .xml File:
<People>
<Person image="Test.jpg" company="" position="" website="" chat="" notes="">
<Name first_name="Max" second_name="" last_name="Mustermann" salutation="" />
<Email email1="" email2="" email3="" />
<Phone_Numbers business="" private="" mobile="" />
<Business_Adress street="" place="" state="" postalcode="" country="" />
<Private_Adress street="" place="" state="" postalcode="" country="" />
</Person>
</People>
And the new Window should be linked to the Name Element of the Person.
Though there will be more then one person, but the window should be linked to the right person.
This is the XmlDataProvider:
<XmlDataProvider x:Name="XmlData" Source="People.xml" XPath="/People/Person" />
And the binding of the listbox looks like this:
<ListBox
Grid.Row="0"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding UpdateSourceTrigger=Explicit}"
x:Name="PeopleListBox"
SelectionMode="Single">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1} {2}">
<Binding XPath="Name/#last_name" />
<Binding XPath="Name/#first_name" />
<Binding XPath="Name/#second_name" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Unless DataContext is set to XmlData your ItemsSource binding won't work. You did not mention where you define XmlDataProvider because if in Resources then you need to specify x:Key instead of x:Name and then you would have to specify it as Binding.Source. You also did not say what is People.xml as it must be added to your solution as a resource
<Window ...>
<Window.Resources>
<XmlDataProvider x:Key="XmlData" XPath="/People/Person" Source="People.xml"/>
</Window.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource XmlData}}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1} {2}">
<Binding XPath="Name/#last_name" />
<Binding XPath="Name/#first_name" />
<Binding XPath="Name/#second_name" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
or you can load it manually by setting XmlDataProvider.Source manually in code

How to get a parent value in 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.

Categories