WPF bind enum to combobox - c#

In my WPF UserControl I need to bind an Enum on a ComboBox. This enum is declared locally:
public partial class ViewerDataConfiguration : UserControl
{
private ViewerDataConfigurationViewModel PageViewModel;
public Visibility IsParametriSelected { get; set; }
public IEnumerable<eDatoAlarmMode> EnumAlarmModes {
get
{
return Enum.GetValues(typeof(eDatoAlarmMode)).Cast<eDatoAlarmMode>();
}
}
On the main Grid, where there is a collection bound, I defined a ComboBox as follows:
<TextBox Grid.Column="16" Text="{Binding ConfigObject.Edit.Source}" Style="{StaticResource txtDataStyle2}" Width="30" Visibility="{Binding ConfigObject.Edit, Converter={StaticResource ListaValoriVisibilityConverter}}" HorizontalAlignment="Stretch" TextChanged="Data_TextChanged" />
<Label Grid.Column="17" Content="AlarmMode" Style="{StaticResource labelStyle2}" />
<ComboBox Grid.Column="18" Width="30"
ItemsSource="{Binding Path=EnumAlarmModes, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:ViewerDataConfiguration}}"
DisplayMemberPath="Value"
SelectedValuePath="Value" Style="{StaticResource comboUsersStyle}" />
Basically seems that my IEnumerable is not bound correctly. I see the elements but they're blank. Any hint?

You are using DisplayMemberPath and SelectedValuePath attributes, but your collection item type is just a simple string, and want to use this entire instance directly, so you should remove these attributes and it should work as expected.
You also need to change the field to a property as data binding works only on properties, not class fields (although x:Bind in UWP no longer has this limitation):
public IEnumerable<AlarmMode> EnumAlarmModes
{
get
{
return Enum.GetValues(typeof(AlarmMode)).Cast<AlarmMode>();
}
}
If you want to display enum values instead of names, create a value converter:
public class EnumValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then use it in the ItemTemplate:
<ComboBox Grid.Column="18" Width="100"
ItemsSource="{Binding Path=EnumAlarmModes}">
<ComboBox.Resources>
<local:EnumValueConverter x:Key="EnumValueConverter" />
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource EnumValueConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
For this to work you also have to add a xmlns:local declaration to your Window:
xmlns:local="clr-namespace:NamespaceOfConverter"

Related

How to pass element in the converter parameter in uwp?

I have to pass the element in the converter parameter in UWP as like below WPF code snippet.
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource captionSummaryRowConverter}, ConverterParameter= {x:Reference Name= dataGrid}}" Foreground="Blue" Background="Yellow" FontSize="15"></TextBlock>
</DataTemplate>
Since x:Reference keyword is not available in the Uwp, Is there any possible way to pass the element to converterparameter in uwp.
How to pass element in the converter parameter in uwp?
Currently, Converter does not support pass element ConverterParameter. We often use it to pass StaticResource or string format.
<TextBlock Text="{Binding DoubleValue,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0:N2}'}"/>
OR
<TextBlock Text="Score : 60" Foreground="{Binding Passed,Converter={StaticResource BoolToValueConverter},ConverterParameter={StaticResource PassedBrush},FallbackValue={StaticResource FailedBrush}}"/>
Update
You could add the property to the Converter and bind with your root panel, then pass the element name as parameter to the Converter, call FindName method to get the element.
public class ImageConverter : IValueConverter
{
public UIElement UIParameter { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{
var rootGrid = UIParameter as Grid;
if(parameter != null)
{
var ele = rootGrid.FindName(parameter.ToString());
}
return value;
}
}
Usage
<local:ImageConverter x:Key="ImageConverter" UIParameter="{x:Bind RootGrid}" />
</Page.Resources>
<Grid x:Name="RootGrid">
<TextBlock x:Name="TestBlock" Text="Hello" />
<Image Source="{Binding Converter={StaticResource ImageConverter}, ConverterParameter='TestBlock'}" />
<ComboBox

Combobox showing error "Binding expression path error"

I have two combobox which are currently binded to the user model. The first combobox supposed to show the current userRole value before clicking on the combobox. The other combobox supposed to show the userStatus either 1 or 0. Now the second combobox is not displaying any value. However the first combobox is displaying the value once its clicked only.
Here is the xaml code:
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12" Text="User Role: " VerticalAlignment="Center" />
<ComboBox x:Name="cbUserRole" FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Center" Loaded="cbUserRole_Loaded" SelectedItem="{Binding UserRole, Mode=TwoWay" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12" Text="User Status: " VerticalAlignment="Center" />
<ComboBox x:Name="cbUserStatus" FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Center" SelectedIndex="{Binding UserStatus, Converter={StaticResource boolToIndexConverter}}" />
</StackPanel>
Here is my converter code:
public class BoolToIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value == true) ? 0 : 1;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((int)value == 0) ? true : false;
}
}
The code above is refered from this link:
Here is my User model code:
private string userrole;
public string UserRole
{
get { return userrole; }
set
{
userrole = value;
OnPropertyChanged("UserRole");
}
}
private bool userstatus;
public bool UserStatus
{
get { return userstatus; }
set
{
userstatus = value;
OnPropertyChanged("UserStatus");
}
}
How can i fix this problems? I did search and tried from different blogs but its not working for me.
The problem is that your combobox's DataContext is User, but you need to set ItemsSource from DataGrid's datacontext. In order to do that you need to use the following syntax:
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}}, Path=DataContext.Users}"
x:Name="cbUserRole" FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Center"
SelectedItem="{Binding UserRole,Mode=TwoWay}"></ComboBox>
And Remove Loaded handler.
You can also use <DataGrid DataContext="{StaticResource uvm}" instead of codebehind code:
var userList = new UserViewModel().Users;
userDataGrid.ItemsSource = userList;
You don't have ItemsSource for your Status Combobox. I think you should add collection of statuses to UserViewModel and bind it like in the example above.

Using a IConverter to deal with {NewItemPlaceholder} in WPF / XAML / MVVM

Here is my DataTemplate:
<UserControl.Resources>
<converter:PlaceholderConverter x:Key="_placeholderConverter"/>
<!-- Data(Display)Template for data objects of x:Type Customer-->
<DataTemplate DataType="{x:Type model:Customer}">
<!-- Customer Properties will be vertically stacked -->
<ContentControl >
<StackPanel>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding LastName}"/>
<TextBlock Text="{Binding Phone}"/>
</StackPanel>
</ContentControl>
</DataTemplate>
<UserControl.Resources>
And the two different 'container's:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0"
Content="Delete"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="75"
Command="{Binding DeleteCommand}"/>
<DataGrid Grid.Row="1"
ItemsSource="{Binding Customers}"
SelectedItem="{Binding SelectedCustomer}"
AutoGenerateColumns="True"/>
<ListBox
Grid.Row="2"
ItemsSource="{Binding Customers, Mode=OneWay}"/>
</Grid>
And the app:
How to remove the {NewItemPlaceholder}? [Done, solution below].
How to prevent the binding error that mention "{NewItemPlaceholder}" when clicking in one of the empty rows in the table above intending on adding a new row (I can still add rows).
The errors:
...Cannot convert '{NewItemPlaceholder}' from type 'NamedObject' to type 'CustomerExample.Model.Customer'...
...ConvertBack cannot convert value '{NewItemPlaceholder}' (type 'NamedObject'). BindingExpression:Path=SelectedCustomer; DataItem='CustomerViewModel'...
I can write an IConverter implementation, but how to tie it in to the XAML?
thanks in advance :-)
Here is the implementation of the IConverter:
public class PlaceholderConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value.ToString() == "{NewItemPlaceholder}")
return DependencyProperty.UnsetValue;
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
and to bind to individual items, the XAML goes something like:
<TextBlock Text="{Binding Name, Converter={StaticResource PlaceholderConverter}}"/>
But I think I need to add it 'globally' to the data collection elements, not to where individual properties are being bound.
You don't need a Binding Converter. Instead, bind the ListBox to a CollectionViewSource that wraps the Custumers collection. The CollectionViewSource skips the NewItemPlaceholder element from the source collection.
<UserControl.Resources>
...
<CollectionViewSource x:Key="CustomersCVS" Source="{Binding Customers}"/>
</UserControl.Resources>
...
<ListBox ItemsSource="{Binding Source={StaticResource CustomersCVS}}"/>
You also don't need a Converter for the SelectedItem Binding. Just set the Binding's TargetNullValue property:
<DataGrid SelectedItem="{Binding SelectedCustomer,
TargetNullValue={x:Static CollectionView.NewItemPlaceholder}}" .../>
Even though the accepted answer is correct for the OP's question I needed a more general approach to prevent the error and still allow users to add rows. As I used this ValueConverter for many different object types I used reflection to determine the target type's constructor and just returned a new object of that type.
public class IgnoreNewItemPlaceHolderConverter : IValueConverter
{
private const string NewItemPlaceholderName = "{NewItemPlaceholder}";
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == DependencyProperty.UnsetValue)
return DependencyProperty.UnsetValue;
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value.ToString() == NewItemPlaceholderName)
{
var ctors = targetType.GetConstructors();
// invoke the first public constructor with no parameters.
return ctors[0].Invoke(new object[] { });
}
return value;
}
}

How to bind a combobox with converter to a list in WPF

Here I'm trying to bind the combobox to a List Codes. The combobox is displaying: A & B
<ComboBox ItemsSource="{Binding Path=Codes}"/>
public SettingsWindow()
{
InitializeComponent();
Codes = new List<Code> {Code.A, Code.B};
DataContext = this;
}
I have defined a converter to display a more understandable info in the combobox:
public class CodeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var code = (Code)value;
string text;
if (code == Code.A)
{
text = "ACI318-99";
}
else
{
text = "ACI318-11";
}
return text;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
But I don't know how to use this converter in my XAML, so that I will have ACI318-99 & ACI318-11 in my combobox.
You should set the ItemTemplate of your Combobox, and use the Converter inside that.
<ComboBox ItemsSource="{Binding Codes}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=., Converter={StaticResource converterInstance}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
here, converterInstance should be an instance of your custom converter in a resource dictionary.
The Caliburn Micro convention is not that different but I just wanted to add it for future searchers. (Path=. is not needed in my case)
<ComboBox x:Name="MyPropertyWithItems">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource converterInstance}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Try
<Window.Resources>
<CodeConverter x:Key="CodeConverter"/>
</Window.Resources>
and
<ComboBox ItemsSource="{Binding Path="Codes" Converter="{StaticResource CodeConverter}}"/>

WPF ListView with group of RadioButtons and select default value

Today I have a problem with selected default CheckBox. But First i show my code:
<ScrollViewer>
<ListView ItemsSource="{Binding itemsSource, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<Label Content="{Binding AttrName, Mode=OneWay}" />
</Expander.Header>
<ListView Margin="20, 0, 0, 0" ItemsSource="{Binding subItemSource}" BorderBrush="Transparent" >
<ListView.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="{Binding DataContext.AttrName, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
Content="{Binding}"
<!-- What should I bind to to get item checked? -->
IsChecked={}/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
So i have a nested binding where in CheckBoxes I bind GroupName to parent data context. My itemsSource contains the following properties:
int DefaultValue { get; set; }
List<int> subItemSource { get; set; }
And all I want now is to mark RadioButton when actual binding value is equal to DefaultValue. How should I do this? Should I write validator?
I'll start by writing a converter class
class ElementComparer : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values[0] == values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
then declare the converter as a resource where l: is your namespace to converter
<l:ElementComparer x:Key="ElementComparer"/>
then in your data template
<DataTemplate>
<RadioButton GroupName="{Binding DataContext.AttrName, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
Content="{Binding}"
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource ElementComparer}" Mode="OneWay">
<Binding Path="DataContext.DefaultValue" RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding />
</MultiBinding>
</RadioButton.IsChecked>
provided the datacontext of the ItemsControl is containing the property for default value to compare with, the trick is to compare the selected item of the list to the current item to detect if it is default item, and will return true from converter and hence radio will be checked

Categories