Switching Binding Sources using a MultiBinding - wpf

I have a DataBinding with a MultiBinding of two ObservableCollections, and i want to switch between them on a condition with a MultiConverter.
So the converter gives the right collection, but the binding doesn't seem to be updated.
Any Ideas??
Greets,
Jürgen

This is the converter you need:
public class SwitchCollectionsConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool b = (bool)values[2];
if (b)
return values[0];
else
return values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
registering the converter:
<local:SwitchCollectionsConverter x:Key="TheConverter" />
usage of the binding:
<ItemsControl>
<ItemsControl.ItemsSource>
<MultiBinding Converter="{StaticResource TheConverter}">
<Binding Path="FirstCollection" />
<Binding Path="SecondCollection" />
<Binding Path="IsFirst" />
</MultiBinding>
</ItemsControl.ItemsSource>
</ItemsControl>
under the assumption that you have a FirstCollection, a SecondCollection, and an IsFirst Properties in the DataContext

Do you need the view to update the source lists?
If so, your binding should be in TwoWay mode:
<TextBox Text="{Binding Source, Mode="TwoWay"}" />

Related

PropertyChangedTrigger For Multiple Properties?

Is there a way to create PropertyChangedTrigger for multiple properties that all share the same conditions, for the sake of redundancy, without specifying the conditions repeatedly for each PropertyChangedTrigger?
For example, when PropertyA, PropertyB and PropertyC change, I want a command to execute in my ViewModel.
I was thinking of something like extending the PropertyChangedClass and adding a dependency property of a an observable collection of Bindings. But it turned out I'm not very knowledgeable in how Bindings are monitored.
Then I saw some of old code and saw Multibinding. I thought it could work. And it did with a MultiValueConverter that just increments a static field. I'm not sure if this is the best solution but this worked.
First the MultiValueConverter:
public class IncrementOnPropertyChangedMultiConverter:IMultiValueConverter
{
static uint _counter=0;
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
_counter = _counter < uint.MaxValue ? _counter + 1 : 0;
return _counter;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Then in XAML you just add a MultiBinding instead of Binding in the PropertyChangedTrigger.
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger>
<ei:PropertyChangedTrigger.Binding>
<MultiBinding Converter="{StaticResource IncrementOnPropertyChangedMultiConverter}">
<Binding Path="Property1" />
<Binding Path="Property2" />
<Binding Path="PropertyN" />
</MultiBinding>
</ei:PropertyChangedTrigger.Binding>
<i:Interaction.Behaviors>
<ei:ConditionBehavior>
<ei:ConditionalExpression>
<ei:ComparisonCondition LeftOperand="{Binding Property1}"
Operator="Equal"
RightOperand="{Binding Property2}" />
</ei:ConditionalExpression>
</ei:ConditionBehavior>
</i:Interaction.Behaviors>
<ei:ChangePropertyAction PropertyName="Background"
Value="Transparent" />
</ei:PropertyChangedTrigger>
<i:Interaction.Triggers>

How to make Converters with bindable ConverterParameters?

Most converters take no parameters, or one fixed parameter, and are easy to bind to:
<local:MyConverter x:Key="MyConverterInstance" />
<TextBox Text="{Binding Path=MyTime,
Converter={StaticResource MyConverterInstance},
ConverterParameter='yyyy/MM/dd'}" />
But if I want that format to be a dynamic property that the user can change, I can't do something like this, right?:
<TextBox Text="{Binding Path=MyTime,
Converter={StaticResource MyConverterInstance},
ConverterParameter={Binding Path=UserFormat}}" />
So my only option is to define a DependencyProperty on MyConverter for binding. But my converter definition is a StaticResource. I can't go
<local:MyConverter x:Key="MyConverterInstance"
Format="{Binding Path=UserFormat}"/>
because there's no DataContext on StaticResources. How can I set this up?
You cannot bind to a converterparameter but you can use Multibinding instead.
For example: http://www.switchonthecode.com/tutorials/wpf-tutorial-using-multibindings
or How to simply bind this to ConverterParameter?
(Alain) So just to translate that linked answer into something that matches this question:
<TextBlock>
<TextBlock.Resources>
<local:MyConverter x:Key="MyConverterInstance" />
</TextBlock.Resources>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="MyTime" />
<Binding Path="UserFormat" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
DateTime time = (DateTime)values[0];
string format = values[1].ToString();
return time.ToString(format);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Enable a Button When any of Checkboxes on Windows is Checked

I have 3 CheckBoxes on Windows. I want to enable Button When any of this CheckBoxes is Checked by using binding. I know tips like this :
<Button IsEnabled={Binding ElementName=CheckBox1,Path=IsChecked} />
but I want button bind to other 2 CheckBox.
How to do this?
you can use MultiBinding with a MultiValueConverter
<Button>
<Button.IsEnabled>
<MultiBinding Converter={StaticResource MultiCheckedToEnabledConverter}>
<Binding ElementName="CheckBox1" Path="IsChecked" />
<Binding ElementName="CheckBox2" Path="IsChecked" />
<Binding ElementName="CheckBox3" Path="IsChecked" />
</MultiBinding>
</Button.IsEnabled>
</Button>
public class MultiCheckedToEnabledConverter : IMultiValueConverter
{
#region Implementation of IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
if (values != null) {
return values.OfType<bool>().Any(b => b);
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
return new object[] {};
}
#endregion
}
hope this helps

Enabling/disabling control based on values of more than one control in WPF

Can I control the enabling/disabling of my textbox using a checkbox and a radio-button, both?
My UI is such that the checkbox controls the individual textbox state, while a radio button controls the enable/disable states of both the textbox and the checkbox.
You can perform this in the UI using a multibinding. This would look like this:
<TextBlock>
<TextBlock.IsEnabled>
<MultiBinding Converter="{StaticResource MultiConverter}">
<Binding ElementName="MyCheckBox" Path="IsChecked" />
<Binding ElementName="MyRadioButton" Path="IsChecked" />
</MultiBinding>
</TextBlock.IsEnabled>
</TextBlock>
<CheckBox x:Name="MyCheckBox"/>
<RadioButton x:Name="MyRadioButton"/>
Where the converter looks at the checked state of the radio and checkbox
public class MultiConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
bool isCheckBoxChecked = (bool)value[0];
bool isRadioButtonChecked = (bool)value[1];
return isCheckBoxChecked && isRadioButtonChecked;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Can't access datacontext in multivalueconverter

I have a usercontrol which I need to set a specific DataContext on. The usercontrol uses multivalueconverters. However, the binding in multivalueconverter fails to use the datacontext. It works fine for regular value converters. Anyone know what is going on? To clarify, below code is not similar to my real code, it's just a sample reproducing my issue. FOr example, my multivalueconverters take more than one argument.
XAML:
<Window x:Class="MultConvTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:MultConvTest="clr-namespace:MultConvTest"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<MultConvTest:MyValueConverter x:Key="MyValueConverter" />
<MultConvTest:MyMultiConverter x:Key="MyMultiConverter" />
</Window.Resources>
<StackPanel>
<!--This works, output is Formatted: 7-->
<TextBlock
DataContext="{Binding Path=Data}"
Text="{Binding Path=Length, Converter={StaticResource MyValueConverter}}" />
<!--This works, output is Formatted: 7-->
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MyMultiConverter}">
<Binding Path="Data.Length" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<!--This fails, the converter never access the DataContext of the textbox-->
<TextBlock
DataContext="{Binding Path=Data}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MyMultiConverter}">
<Binding Path="Length" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
namespace MultConvTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = this;
}
public string Data
{
get { return "My Text"; }
}
}
public class MyValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int number = (int)value;
return string.Format("Formatted: {0}", number);
}
public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class MyMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// This will fail when DataContext is set. values[0] is of type Ms.Internal.NamedObject
// with value DependencyProperty.UnsetValue.
int number = (int)values[0];
return string.Format("Formatted: {0}", number);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
You are not going to like this ;-)
Simply put the line DataContext=this before InitializeComponent()
It seems the MultiConverter for the third textbox is triggered before the global DataContext is set, thus in effect producing a null reference error...

Resources