I have a Control that I want to automatically disappear if another control has no visibile children. I'm not sure how to implement that though. I feel as though I need to create a binding that returns bindings for each child element's visible property and then aggregates them into a MultiValueConverter. I think it is working but it seems as though when I add items to my collection, the collection binding isn't being re-evaluated. Has anyone done this before?
Below is my code:
<Grid.Resources>
<local:BindingExpander x:Key="BindingExpander"/>
<local:TestConverter x:Key="TestConverter" />
</Grid.Resources>
<Button Content="Button" HorizontalAlignment="Left" Margin="237,166,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click">
<Button.Visibility>
<MultiBinding Converter="{StaticResource TestConverter}">
<Binding ElementName="lstItems" Path="Items" Converter="{StaticResource BindingExpander}" ConverterParameter="Visibility"/>
</MultiBinding>
</Button.Visibility>
</Button>
<ListBox x:Name="lstItems" HorizontalAlignment="Left" Height="100" Margin="601,130,0,0" VerticalAlignment="Top" Width="100" DisplayMemberPath="Content"/>
and:
public class TestConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
var ret = Visibility.Collapsed;
foreach (var item in values) {
if(item is IEnumerable IE) {
foreach (var Child in IE) {
}
}
}
return ret;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
public class BindingExpander : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var ret = new List<Binding>();
if(value is IEnumerable IE) {
foreach (var item in IE) {
ret.Add(new Binding(parameter.ToString()) {
Source = item,
Mode = BindingMode.OneWay
});
}
}
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
````
I have a Control that I want to automatically disappear if another
control has no visibile children..
Simply create a Boolean property which reports the status of what the other control is binding to such as:
public bool HasItems { get { return _SomeArray?.Any(); }}
This property can be as elaborate as needed, but a basic one above for the example is shown.
Then bind the visibility flag of the control in question to the HasItems.
Note that the HasItems does not have the plumbing for INotifyPropertyChanged. In the code(s) where items are added to the _SomeArray simply put in a call to PropertyChanged("HasItems")
On my blog I provide a basic example of that (Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding) which looks like this where someone would bind to IsMemebershipAtMax such as what you are doing:
public bool IsMembershipAtMax
{
get { return MemberCount > 3; }
}
public int MemberCount
{
get { return _MemberCount; }
set
{
_MemberCount = value;
OnPropertyChanged();
OnPropertyChanged("IsMembershipAtMax");
}
}
public List<string> Members
{
get { return _Members; }
set { _Members = value; OnPropertyChanged(); }
}
I have multiple canvas images of different types (image source, geometry, path) and wish to only show 1 depending on a string binding.
whats the best way to do this?
i'd like it to be reusable so i can place this code inside a user control and then have many of these images around the app and i select which 1 is shown.
Like so:
<CanvasImage Image="Pie"/>
<CanvasImage Image="Dog"/>
Would it be too computationally expensive to have them all declared in the user control view and use visibility bindings
Pie canvas example:
<canvas>
<Data ="m24,98,07">
</canvas>
Dog canvas example:
<canvas>
<image source="">
<canvas>
This converter return an image source directly, depending on the value it receives.
namespace TestTreeView.Views
{
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string file = "";
string v = value as string;
switch (v)
{
case "Pie":
file = #".\path\to\your\pie.jpg";
break;
case "Dog":
file = #".\path\to\your\dog.jpg";
break;
default:
return null;
}
return new BitmapImage(new Uri(file, UriKind.RelativeOrAbsolute));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Usage in XAML:
<Window xmlns:local="clr-namespace:YourNamespace.Views" ...>
<Window.Resources>
<local:StringToImageConverter x:Key="stringToImageConverter"/>
</Window.Resources>
<Grid>
<Canvas>
<Image Source="{Binding YourString, Converter={StaticResource stringToImageConverter}}"/>
</Canvas>
</Grid>
</Window>
Original answer
I think you need to use a Converter.
It will take a ConverterParameter, a String, that will tell what the binded value is expected to be, and return a Visiblity to indicate if the canvas should be visible or not.
namespace YourNamespace.Views
{
public class StringToCanvasVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string v = value as string;
string p = parameter as string;
return (v != null && p != null && v == p) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Usage in XAML:
<Window xmlns:local="clr-namespace:YourNamespace.Views" ...>
<Window.Resources>
<local:StringToVisibilityConverter x:Key="stringToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Canvas Visibility="{Binding YourString, Converter={StaticResource stringToVisibilityConverter}, ConverterParameter=Pie}"/>
<Canvas Visibility="{Binding YourString, Converter={StaticResource stringToVisibilityConverter}, ConverterParameter=Dog}"/>
</Grid>
</Window>
I've got an enum like this:
public enum MyLovelyEnum
{
FirstSelection,
TheOtherSelection,
YetAnotherOne
};
I got a property in my DataContext:
public MyLovelyEnum VeryLovelyEnum { get; set; }
And I got three RadioButtons in my WPF client.
<RadioButton Margin="3">First Selection</RadioButton>
<RadioButton Margin="3">The Other Selection</RadioButton>
<RadioButton Margin="3">Yet Another one</RadioButton>
Now how do I bind the RadioButtons to the property for a proper two-way binding?
You can further simplify the accepted answer. Instead of typing out the enums as strings in xaml and doing more work in your converter than needed, you can explicitly pass in the enum value instead of a string representation, and as CrimsonX commented, errors get thrown at compile time rather than runtime:
ConverterParameter={x:Static local:YourEnumType.Enum1}
<StackPanel>
<StackPanel.Resources>
<local:ComparisonConverter x:Key="ComparisonConverter" />
</StackPanel.Resources>
<RadioButton IsChecked="{Binding Path=YourEnumProperty, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static local:YourEnumType.Enum1}}" />
<RadioButton IsChecked="{Binding Path=YourEnumProperty, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static local:YourEnumType.Enum2}}" />
</StackPanel>
Then simplify the converter:
public class ComparisonConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value?.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value?.Equals(true) == true ? parameter : Binding.DoNothing;
}
}
Edit (Dec 16 '10):
Thanks to anon for suggesting returning Binding.DoNothing rather than DependencyProperty.UnsetValue.
Note - Multiple groups of RadioButtons in same container (Feb 17 '11):
In xaml, if radio buttons share the same parent container, then selecting one will de-select all other's within that container (even if they are bound to a different property). So try to keep your RadioButton's that are bound to a common property grouped together in their own container like a stack panel. In cases where your related RadioButtons cannot share a single parent container, then set the GroupName property of each RadioButton to a common value to logically group them.
Edit (Apr 5 '11):
Simplified ConvertBack's if-else to use a Ternary Operator.
Note - Enum type nested in a class (Apr 28 '11):
If your enum type is nested in a class (rather than directly in the namespace), you might be able to use the '+' syntax to access the enum in XAML as stated in a (not marked) answer to the question :
ConverterParameter={x:Static local:YourClass+YourNestedEnumType.Enum1}
Due to this Microsoft Connect Issue, however, the designer in VS2010 will no longer load stating "Type 'local:YourClass+YourNestedEnumType' was not found.", but the project does compile and run successfully. Of course, you can avoid this issue if you are able to move your enum type to the namespace directly.
Edit (Jan 27 '12):
If using Enum flags, the converter would be as follows:
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((Enum)value).HasFlag((Enum)parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
Edit (May 7 '15):
In case of a Nullable Enum (that is **not** asked in the question, but can be needed in some cases, e.g. ORM returning null from DB or whenever it might make sense that in the program logic the value is not provided), remember to add an initial null check in the Convert Method and return the appropriate bool value, that is typically false (if you don't want any radio button selected), like below:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) {
return false; // or return parameter.Equals(YourEnumType.SomeDefaultValue);
}
return value.Equals(parameter);
}
Note - NullReferenceException (Oct 10 '18):
Updated the example to remove the possibility of throwing a NullReferenceException. `IsChecked` is a nullable type so returning `Nullable` seems a reasonable solution.
You could use a more generic converter
public class EnumBooleanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object parameterValue = Enum.Parse(value.GetType(), parameterString);
return parameterValue.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
return Enum.Parse(targetType, parameterString);
}
#endregion
}
And in the XAML-Part you use:
<Grid>
<Grid.Resources>
<l:EnumBooleanConverter x:Key="enumBooleanConverter" />
</Grid.Resources>
<StackPanel >
<RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=FirstSelection}">first selection</RadioButton>
<RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=TheOtherSelection}">the other selection</RadioButton>
<RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=YetAnotherOne}">yet another one</RadioButton>
</StackPanel>
</Grid>
For the EnumToBooleanConverter answer:
Instead of returning DependencyProperty.UnsetValue consider returning Binding.DoNothing for the case where the radio button IsChecked value becomes false.
The former indicates a problem (and might show the user a red rectangle or similar validation indicators) while the latter just indicates that nothing should be done, which is what is wanted in that case.
http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.convertback.aspx
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.donothing.aspx
I would use the RadioButtons in a ListBox, and then bind to the SelectedValue.
This is an older thread about this topic, but the base idea should be the same: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/323d067a-efef-4c9f-8d99-fecf45522395/
For UWP, it is not so simple: You must jump through an extra hoop to pass a field value as a parameter.
Example 1
Valid for both WPF and UWP.
<MyControl>
<MyControl.MyProperty>
<Binding Converter="{StaticResource EnumToBooleanConverter}" Path="AnotherProperty">
<Binding.ConverterParameter>
<MyLibrary:MyEnum>Field</MyLibrary:MyEnum>
</Binding.ConverterParameter>
</MyControl>
</MyControl.MyProperty>
</MyControl>
Example 2
Valid for both WPF and UWP.
...
<MyLibrary:MyEnum x:Key="MyEnumField">Field</MyLibrary:MyEnum>
...
<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={StaticResource MyEnumField}}"/>
Example 3
Valid only for WPF!
<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static MyLibrary:MyEnum.Field}}"/>
UWP doesn't support x:Static so Example 3 is out of the question; assuming you go with Example 1, the result is more verbose code. Example 2 is slightly better, but still not ideal.
Solution
public abstract class EnumToBooleanConverter<TEnum> : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var Parameter = parameter as string;
if (Parameter == null)
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(typeof(TEnum), value) == false)
return DependencyProperty.UnsetValue;
return Enum.Parse(typeof(TEnum), Parameter).Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
var Parameter = parameter as string;
return Parameter == null ? DependencyProperty.UnsetValue : Enum.Parse(typeof(TEnum), Parameter);
}
}
Then, for each type you wish to support, define a converter that boxes the enum type.
public class MyEnumToBooleanConverter : EnumToBooleanConverter<MyEnum>
{
//Nothing to do!
}
The reason it must be boxed is because there's seemingly no way to reference the type in the ConvertBack method; the boxing takes care of that. If you go with either of the first two examples, you can just reference the parameter type, eliminating the need to inherit from a boxed class; if you wish to do it all in one line and with least verbosity possible, the latter solution is ideal.
Usage resembles Example 2, but is, in fact, less verbose.
<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource MyEnumToBooleanConverter}, ConverterParameter=Field}"/>
The downside is you must define a converter for each type you wish to support.
I've created a new class to handle binding RadioButtons and CheckBoxes to enums. It works for flagged enums (with multiple checkbox selections) and non-flagged enums for single-selection checkboxes or radio buttons. It also requires no ValueConverters at all.
This might look more complicated at first, however, once you copy this class into your project, it's done. It's generic so it can easily be reused for any enum.
public class EnumSelection<T> : INotifyPropertyChanged where T : struct, IComparable, IFormattable, IConvertible
{
private T value; // stored value of the Enum
private bool isFlagged; // Enum uses flags?
private bool canDeselect; // Can be deselected? (Radio buttons cannot deselect, checkboxes can)
private T blankValue; // what is considered the "blank" value if it can be deselected?
public EnumSelection(T value) : this(value, false, default(T)) { }
public EnumSelection(T value, bool canDeselect) : this(value, canDeselect, default(T)) { }
public EnumSelection(T value, T blankValue) : this(value, true, blankValue) { }
public EnumSelection(T value, bool canDeselect, T blankValue)
{
if (!typeof(T).IsEnum) throw new ArgumentException($"{nameof(T)} must be an enum type"); // I really wish there was a way to constrain generic types to enums...
isFlagged = typeof(T).IsDefined(typeof(FlagsAttribute), false);
this.value = value;
this.canDeselect = canDeselect;
this.blankValue = blankValue;
}
public T Value
{
get { return value; }
set
{
if (this.value.Equals(value)) return;
this.value = value;
OnPropertyChanged();
OnPropertyChanged("Item[]"); // Notify that the indexer property has changed
}
}
[IndexerName("Item")]
public bool this[T key]
{
get
{
int iKey = (int)(object)key;
return isFlagged ? ((int)(object)value & iKey) == iKey : value.Equals(key);
}
set
{
if (isFlagged)
{
int iValue = (int)(object)this.value;
int iKey = (int)(object)key;
if (((iValue & iKey) == iKey) == value) return;
if (value)
Value = (T)(object)(iValue | iKey);
else
Value = (T)(object)(iValue & ~iKey);
}
else
{
if (this.value.Equals(key) == value) return;
if (!value && !canDeselect) return;
Value = value ? key : blankValue;
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And for how to use it, let's say you have an enum for running a task manually or automatically, and can be scheduled for any days of the week, and some optional options...
public enum StartTask
{
Manual,
Automatic
}
[Flags()]
public enum DayOfWeek
{
Sunday = 1 << 0,
Monday = 1 << 1,
Tuesday = 1 << 2,
Wednesday = 1 << 3,
Thursday = 1 << 4,
Friday = 1 << 5,
Saturday = 1 << 6
}
public enum AdditionalOptions
{
None = 0,
OptionA,
OptionB
}
Now, here's how easy it is to use this class:
public class MyViewModel : ViewModelBase
{
public MyViewModel()
{
StartUp = new EnumSelection<StartTask>(StartTask.Manual);
Days = new EnumSelection<DayOfWeek>(default(DayOfWeek));
Options = new EnumSelection<AdditionalOptions>(AdditionalOptions.None, true, AdditionalOptions.None);
}
public EnumSelection<StartTask> StartUp { get; private set; }
public EnumSelection<DayOfWeek> Days { get; private set; }
public EnumSelection<AdditionalOptions> Options { get; private set; }
}
And here's how easy it is to bind checkboxes and radio buttons with this class:
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<!-- Using RadioButtons for exactly 1 selection behavior -->
<RadioButton IsChecked="{Binding StartUp[Manual]}">Manual</RadioButton>
<RadioButton IsChecked="{Binding StartUp[Automatic]}">Automatic</RadioButton>
</StackPanel>
<StackPanel Orientation="Horizontal">
<!-- Using CheckBoxes for 0 or Many selection behavior -->
<CheckBox IsChecked="{Binding Days[Sunday]}">Sunday</CheckBox>
<CheckBox IsChecked="{Binding Days[Monday]}">Monday</CheckBox>
<CheckBox IsChecked="{Binding Days[Tuesday]}">Tuesday</CheckBox>
<CheckBox IsChecked="{Binding Days[Wednesday]}">Wednesday</CheckBox>
<CheckBox IsChecked="{Binding Days[Thursday]}">Thursday</CheckBox>
<CheckBox IsChecked="{Binding Days[Friday]}">Friday</CheckBox>
<CheckBox IsChecked="{Binding Days[Saturday]}">Saturday</CheckBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<!-- Using CheckBoxes for 0 or 1 selection behavior -->
<CheckBox IsChecked="{Binding Options[OptionA]}">Option A</CheckBox>
<CheckBox IsChecked="{Binding Options[OptionB]}">Option B</CheckBox>
</StackPanel>
</StackPanel>
When the UI loads, the "Manual" radio button will be selected and you can alter your selection between "Manual" or "Automatic" but either one of them must always be selected.
Every day of the week will be unchecked, but any number of them can be checked or unchecked.
"Option A" and "Option B" will both initially be unchecked. You can check one or the other, checking one will uncheck the other (similar to RadioButtons), but now you can also uncheck both of them (which you cannot do with WPF's RadioButton, which is why CheckBox is being used here)
This work for Checkbox too.
public class EnumToBoolConverter:IValueConverter
{
private int val;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int intParam = (int)parameter;
val = (int)value;
return ((intParam & val) != 0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
val ^= (int)parameter;
return Enum.Parse(targetType, val.ToString());
}
}
Binding a single enum to multiple checkboxes.
You can create the radio buttons dynamically, ListBox can help you do that, without converters, quite simple.
The concrete steps are below:
create a ListBox and set the ItemsSource for the listbox as the enum MyLovelyEnum and binding the SelectedItem of the ListBox to the VeryLovelyEnum property.
then the Radio Buttons for each ListBoxItem will be created.
Step 1: add the enum to static resources for your Window, UserControl or Grid etc.
<Window.Resources>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type system:Enum}"
x:Key="MyLovelyEnum">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:MyLovelyEnum" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Step 2: Use the List Box and Control Template to populate each item inside as Radio button
<ListBox ItemsSource="{Binding Source={StaticResource MyLovelyEnum}}" SelectedItem="{Binding VeryLovelyEnum, Mode=TwoWay}" >
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<RadioButton
Content="{TemplateBinding ContentPresenter.Content}"
IsChecked="{Binding Path=IsSelected,
RelativeSource={RelativeSource TemplatedParent},
Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
The advantage is: if someday your enum class changes, you do not need to update the GUI (XAML file).
References:
https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/
One way to handle this would be to have separate bool properties in your ViewModel class. Here is how I've handled with such a situation:
ViewModel:
public enum MyLovelyEnum { FirstSelection, TheOtherSelection, YetAnotherOne };
private MyLovelyEnum CurrentSelection;
public bool FirstSelectionProperty
{
get
{
return CurrentSelection == MyLovelyEnum.FirstSelection;
}
set
{
if (value)
CurrentSelection = MyLovelyEnum.FirstSelection;
}
}
public bool TheOtherSelectionProperty
{
get
{
return CurrentSelection == MyLovelyEnum.TheOtherSelection;
}
set
{
if (value)
CurrentSelection = MyLovelyEnum.TheOtherSelection;
}
}
public bool YetAnotherOneSelectionProperty
{
get
{
return CurrentSelection == MyLovelyEnum.YetAnotherOne;
}
set
{
if (value)
CurrentSelection = MyLovelyEnum.YetAnotherOne;
}
}
XAML:
<RadioButton IsChecked="{Binding SimilaritySort, Mode=TwoWay}">Similarity</RadioButton>
<RadioButton IsChecked="{Binding DateInsertedSort, Mode=TwoWay}">Date Inserted</RadioButton>
<RadioButton IsChecked="{Binding DateOfQuestionSort, Mode=TwoWay}">Date of Question</RadioButton>
<RadioButton IsChecked="{Binding DateModifiedSort, Mode=TwoWay}">Date Modified</RadioButton>
It's not as robust or dynamic as some of the other solutions, but the nice thing is it's very self-contained and doesn't require creating custom converters or anything like that.
Based on the EnumToBooleanConverter from Scott.
I noticed that the ConvertBack method doesn't work on the Enum with flags code.
I've tried the following code:
public class EnumHasFlagToBooleanConverter : IValueConverter
{
private object _obj;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
_obj = value;
return ((Enum)value).HasFlag((Enum)parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value.Equals(true))
{
if (((Enum)_obj).HasFlag((Enum)parameter))
{
// Do nothing
return Binding.DoNothing;
}
else
{
int i = (int)_obj;
int ii = (int)parameter;
int newInt = i+ii;
return (NavigationProjectDates)newInt;
}
}
else
{
if (((Enum)_obj).HasFlag((Enum)parameter))
{
int i = (int)_obj;
int ii = (int)parameter;
int newInt = i-ii;
return (NavigationProjectDates)newInt;
}
else
{
// do nothing
return Binding.DoNothing;
}
}
}
}
The only thing that I can't get to work is to do a cast from int to targetType so I made it hardcoded to NavigationProjectDates, the enum that I use. And, targetType == NavigationProjectDates...
Edit for more generic Flags Enum converter:
public class FlagsEnumToBooleanConverter : IValueConverter {
private int _flags=0;
public object Convert(object value, Type targetType, object parameter, string language) {
if (value == null) return false;
_flags = (int) value;
Type t = value.GetType();
object o = Enum.ToObject(t, parameter);
return ((Enum)value).HasFlag((Enum)o);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value?.Equals(true) ?? false) {
_flags = _flags | (int) parameter;
}
else {
_flags = _flags & ~(int) parameter;
}
return _flags;
}
}
A TwoWay Binding solution to UWP that takes the usage of Nullable:
C# Part:
public class EnumConverter : IValueConverter
{
public Type EnumType { get; set; }
public object Convert(object value, Type targetType, object parameter, string lang)
{
if (parameter is string enumString)
{
if (!Enum.IsDefined(EnumType, value)) throw new ArgumentException("value must be an Enum!");
var enumValue = Enum.Parse(EnumType, enumString);
return enumValue.Equals(value);
}
return value.Equals(Enum.ToObject(EnumType,parameter));
}
public object ConvertBack(object value, Type targetType, object parameter, string lang)
{
if (parameter is string enumString)
return value?.Equals(true) == true ? Enum.Parse(EnumType, enumString) : null;
return value?.Equals(true) == true ? Enum.ToObject(EnumType, parameter) : null;
}
}
Here the null value acts as the Binding.DoNothing.
private YourEnum? _yourEnum = YourEnum.YourDefaultValue; //put a default value here
public YourEnum? YourProperty
{
get => _yourEnum;
set{
if (value == null) return;
_yourEnum = value;
}
}
Xaml Part:
...
<Page.Resources>
<ResourceDictionary>
<helper:EnumConverter x:Key="YourConverter" EnumType="yournamespace:YourEnum" />
</ResourceDictionary>
</Page.Resources>
...
<RadioButton GroupName="YourGroupName" IsChecked="{Binding Converter={StaticResource YourConverter}, Mode=TwoWay, Path=YourProperty, ConverterParameter=YourEnumString}">
First way (parameter of type string)
</RadioButton>
<RadioButton GroupName="LineWidth">
<RadioButton.IsChecked>
<Binding
Converter="{StaticResource PenWidthConverter}"
Mode="TwoWay" Path="PenWidth">
<Binding.ConverterParameter>
<yournamespace:YourEnum>YourEnumString</yournamespace:YourEnum>
</Binding.ConverterParameter>
</Binding>
</RadioButton.IsChecked>
Second way (parameter of type YourEnum (actually it was converted to int when passed to converter))
</RadioButton>
Public Class View
Public Property Items As String() = {"One", "Two", "Three"}
Public Property Index As Integer = 0
End Class
It's instance is set as DataContext of this XAML:
<Window>
<StackPanel>
<ListBox ItemsSource="{Binding Items}" SelectedIndex="{Binding Index}"/>
<Label Content="{Binding Items[Index]}"/>
</StackPanel>
</Window>
But this doesn't work.
<Label Content="{Binding Items[{Binding Index}]}"/>
This neither.
<Label Content="{Binding Items[0]}"/>
This works.
Is there any solution except making extra property in view? Something directly in XAML?
I'm afraid it's not possible without some code-behind, but using reflection and dynamic, you can create a converter that can do this (it would be possible without dynamic, but more complex):
public class IndexerConverter : IValueConverter
{
public string CollectionName { get; set; }
public string IndexName { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Type type = value.GetType();
dynamic collection = type.GetProperty(CollectionName).GetValue(value, null);
dynamic index = type.GetProperty(IndexName).GetValue(value, null);
return collection[index];
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Put following into resources:
<local:IndexerConverter x:Key="indexerConverter" CollectionName="Items" IndexName="Index" />
and use it like this:
<Label Content="{Binding Converter={StaticResource indexerConverter}}"/>
EDIT: The previous solution doesn't update properly when the values change, this one does:
public class IndexerConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
{
return ((dynamic)value[0])[(dynamic)value[1]];
}
public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
In resources:
<local:IndexerConverter x:Key="indexerConverter"/>
Usage:
<Label>
<MultiBinding Converter="{StaticResource indexerConverter}">
<Binding Path="Items"/>
<Binding Path="Index"/>
</MultiBinding>
</Label>
What you write in the binding markup extension is assigned to the Path property by default, this property is a string so any dynamic content you refer to inside it will not be evaluated. There is no simple XAML-only method to do what you try to do.
Why don't use this:
<StackPanel>
<ListBox Name="lsbItems" ItemsSource="{Binding Items}" SelectedIndex="{Binding Index}"/>
<Label Content="{Binding ElementName=lsbItems, Path=SelectedItem}"/>
</StackPanel>
In a related question I asked about binding to a particular element of an array indexed by another property. The answer provided worked really well for the sample code example provided.
Where I'm running into trouble is that I specify an ItemSource for a ListBox and I get a DependencyProperty.UnsetValue in my converter when I step through it. No doubt this is a problem with my understanding of Binding.
My ListBox looks like so:
<ListBox ItemsSource="{Binding Path=MyList}">
<ListBox.Resources>
<local:FoodIndexConverter x:Key="indexConverter" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource indexConverter}">
<Binding Path="MyIndex" />
<Binding Path="Fields" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and the code behind looks as so:
public MainWindow()
{
InitializeComponent();
MyList.Add(new SomeData() { Fields = new object[] {"liver", "onions", "cake" } } );
MyList.Add(new SomeData() { Fields = new object[] {"liver", "onions", "candy" } } );
MyList.Add(new SomeData() { Fields = new object[] {"liver", "onions", "pie" } } );
DataContext = this;
}
MyList is a List.
MyIndex is an int.
The converter code is
public class FoodIndexConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values == null || values.Length != 2)
return null;
int? idx = values[0] as int?;
object[] food = values[1] as object[];
if (!idx.HasValue || food == null)
return null;
return food[idx.Value];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
When I step through the debugger in the converter code the MyIndex (value[0]) is DependencyProperty.UnsetValue - the object array is as I'd expect.
I'm assuming it is a Binding issue with:
in that it doesn't know what MyIndex is.
If MyIndex were a property of the SomeData class, it works as I would expect but it's not, it's a property of the class MainWindow, just like MyList.
How do I specify that I'm interested in the MyIndex property that is part of my DataContext and not the MyData List?
It is looking for the MyIndex property on the ListBoxItem (in this case, a SomeData class) and that property doesn't exist.
Set the ElementName in your binding to the window name to get it to look for the property elsewhere.
<Binding ElementName=RootWindow, Path=DataContext.MyIndex />