Cant bind enum to combobox wpf mvvm - c#

A have read a lot of method about the ways of binding enum to combobox. So now in .Net 4.5 it should be pretty ease. But my code dont work.
Dont really understand why.
xaml:
<Window x:Class="SmartTrader.Windows.SyncOfflineDataWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SyncOfflineDataWindow" Height="300" Width="300">
<Grid>
<StackPanel>
<ComboBox ItemsSource="{Binding StrategyTypes}" SelectedItem="{Binding StrategyType}" />
<Button Width="150" Margin="5" Padding="5" Click="Button_Click">Save</Button>
</StackPanel>
</Grid>
xaml.cs backend
namespace SmartTrader.Windows
{
/// <summary>
/// Interaction logic for SyncOfflineDataWindow.xaml
/// </summary>
public partial class SyncOfflineDataWindow : Window
{
public SyncOfflineDataWindow(IPosition position, ContractType type)
{
DataContext = new ObservablePosition(position);
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
}
View Model:
namespace SmartTrader.Entity
{
public class ObservablePosition : NotifyPropertyChanged, IPosition
{
public IEnumerable<StrategyType> StrategyTypes =
Enum.GetValues(typeof (StrategyType)).Cast<StrategyType>();
public ObservablePosition(IPosition position)
{
Strategy = position.Strategy;
}
private StrategyType _strategyType = StrategyType.None;
public StrategyType Strategy
{
get { return _strategyType; }
set
{
_strategyType = value;
OnPropertyChanged();
}
}
}
}
StrategyType is enum.
All i have got it is empty dropdown list

You are trying to bind to a private variable, instead, your enum should be exposed as a Property.
public IEnumerable<StrategyTypes> StrategyTypes
{
get
{
return Enum.GetValues(typeof(StrategyType)).Cast<StrategyType>();
}
}
Also, Discosultan has already solved another problem for you.

Simplest way to bind any enum data to combobox in wpf XAML:
Add data provider in window or user control resource
xmlns:pro="clr-namespace:TestProject">
<UserControl.Resources>
<ObjectDataProvider x:Key="getDataFromEnum" MethodName="GetValues" ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="pro:YourEnumName"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</UserControl.Resources>
<!--ComboBox xaml:-->
<ComboBox ItemsSource="{Binding Source={StaticResource getDataFromEnum}}"/>

I have found a solution on youtube. Check the link below:
How to Bind an Enum to a ComboBox in WPF: https://youtu.be/Bp5LFXjwtQ0
This solution creates a new class derived from MarkupExtension class and uses this class as a source in the XAML code.
EnumBindingSourceExtention.cs file:
namespace YourProject.Helper
{
public class EnumBindingSourceExtention : MarkupExtension
{
public Type EnumType { get; private set; }
public EnumBindingSourceExtention(Type enumType)
{
if (enumType == null || !enumType.IsEnum)
{
throw new Exception("EnumType is null or not EnumType");
}
this.EnumType = enumType;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Enum.GetValues(EnumType);
}
}
}
View.Xaml file:
<Window
xmlns:helper="clr-namespace:YourProject.Helper"/>
<ComboBox
ItemsSource="{Binding Source={helper:EnumBindingSourceExtention {x:Type local:TheEnumClass}}}" />
local:TheEnumClass: TheEumClass should be located where you specify the namespace (in this case, it is on local)

Related

WPF Usercontrol Bindings with MVVM ViewModel not working

I've spent some time trying to solve this problem but couldn't find a solution.
I am trying to bind commands and data inside an user control to my view model. The user control is located inside a window for navigation purposes.
For simplicity I don't want to work with Code-Behind (unless it is unavoidable) and pass all events of the buttons via the ViewModel directly to the controller. Therefore code-behind is unchanged everywhere.
The problem is that any binding I do in the UserControl is ignored.
So the corresponding controller method is never called for the command binding and the data is not displayed in the view for the data binding. And this although the DataContext is set in the controllers.
Interestingly, if I make the view a Window instead of a UserControl and call it initially, everything works.
Does anyone have an idea what the problem could be?
Window.xaml (shortened)
<Window x:Class="Client.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client.Views"
mc:Ignorable="d">
<Window.Resources>
<local:SubmoduleSelector x:Key="TemplateSelector" />
</Window.Resources>
<Grid>
<StackPanel>
<Button Command="{Binding OpenUserControlCommand}"/>
</StackPanel>
<ContentControl Content="{Binding ActiveViewModel}" ContentTemplateSelector="{StaticResource TemplateSelector}">
<ContentControl.Resources>
<DataTemplate x:Key="userControlTemplate">
<local:UserControl />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Grid>
</Window>
MainWindowViewModel (shortened)
namespace Client.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
private ViewModelBase mActiveViewModel;
public ICommand OpenUserControlCommand { get; set; }
public ViewModelBase ActiveViewModel
{
get { return mActiveViewModel; }
set
{
if (mActiveViewModel == value)
return;
mActiveViewModel = value;
OnPropertyChanged("ActiveViewModel");
}
}
}
}
MainWindowController (shortened)
namespace Client.Controllers
{
public class MainWindowController
{
private readonly MainWindow mView;
private readonly MainWindowViewModel mViewModel;
public MainWindowController(MainWindowViewModel mViewModel, MainWindow mView)
{
this.mViewModel = mViewModel;
this.mView = mView;
this.mView.DataContext = mViewModel;
this.mViewModel.OpenUserControlCommand = new RelayCommand(ExecuteOpenUserControlCommand);
}
private void OpenUserControlCommand(object obj)
{
var userControlController = Container.Resolve<UserControlController>(); // Get Controller instance with dependency injection
mViewModel.ActiveViewModel = userControlController.Initialize();
}
}
}
UserControlSub.xaml (shortened)
<UserControl x:Class="Client.Views.UserControlSub"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client.Views"
xmlns:viewModels="clr-namespace:Client.ViewModels"
mc:Ignorable="d">
<Grid>
<ListBox ItemsSource="{Binding Models}" SelectedItem="{Binding SelectedModel}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Attr}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel>
<Button Command="{Binding Add}">Kategorie hinzufügen</Button>
</StackPanel>
</Grid>
</UserControl>
UserControlViewModel (shortened)
namespace Client.ViewModels
{
public class UserControlViewModel : ViewModelBase
{
private Data _selectedModel;
public ObservableCollection<Data> Models { get; set; } = new ObservableCollection<Data>();
public Data SelectedModel
{
get => _selectedModel;
set
{
if (value == _selectedModel) return;
_selectedModel= value;
OnPropertyChanged("SelectedModel");
}
}
public ICommand Add { get; set; }
}
}
UserControlController (shortened)
namespace Client.Controllers
{
public class UserControlController
{
private readonly UserControlSub mView;
private readonly UserControlViewModel mViewModel;
public UserControlController(UserControlViewModel mViewModel, UserControlSub mView)
{
this.mViewModel = mViewModel;
this.mView = mView;
this.mView.DataContext = mViewModel;
this.mViewModel.Add = new RelayCommand(ExecuteAddCommand);
}
private void ExecuteAddCommand(object obj)
{
Console.WriteLine("This code gets never called!");
}
public override ViewModelBase Initialize()
{
foreach (var mod in server.GetAll())
{
mViewModel.Models.Add(mod);
}
return mViewModel;
}
}
}

Stuck with simple data binding in XAML

I have list of strings that I want to use in several ComboBox controls in different tabs of a TabControl. The list of strings is in a public ObservableCollection. The problem is, that I can't get this collection to show in the <Window.Resources> section of the XAML file. Here is the code in a clean new application:
using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace APX_Interface
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public ObservableCollection<String> MyStringList; // not initialized yet
public class NameList : ObservableCollection<String>
{
public NameList() : base()
{
Add("Willam");
Add("Isak");
Add("Victor");
Add("Jules");
}
}
}
}
Here is the XAML:
Window x:Class="APX_Interface.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:APX_Interface"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:MyStringList x:Key="Strings"/>
<local:NameList x:Key="Names"/>
</Window.Resources>
<Grid>
</Grid>
The only thing that shows up for autocomplete when I type local: is App. Compiling the code I get several errors like this:
Error The tag 'MyStringList' does not exist in XML namespace 'clr-namespace:APX_Interface'.
Error XDG0008 The name "MyStringList" does not exist in the namespace "clr-namespace:APX_Interface".
Error XLS0414 The type 'local:MyStringList' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.
The same errors for the class NameList
After looking at many examples, and other discussions here I can't pinpoint what I'm missing.
Thanks for all the suggestions, unfortunately we haven't resolved this issue yet.
I'm posting here the code with the changes suggested and my comments:
using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace APX_Interface
{
public class NameList : ObservableCollection<String>
// This class is now THE ONLY thing visible in <Window.Resources>
// but not any instance of the class.
{
public NameList() : base()
{
}
}
public partial class MainWindow : Window
{
// This instance of the class is NOT visible in <Window.Resources> ???
public NameList MyNames { get { return _names;} }
// nor is this
public ObservableCollection<String> MyNumbers { get { return _numbers; } }
// nope
public NameList _names = null;
//Neither this one
public ObservableCollection<String> _numbers = null;
public MainWindow()
{
InitializeComponent();
// this doesn't make any difference
DataContext = this;
_names = new NameList();
_names.Add("fellow"); // populate dynamically
var _nr = new string[] // static strings
{
"One",
"Two",
"etc.",
};
_numbers = new ObservableCollection<string>(_nr);
}
}
}
A few examples of accessing the collection in XAML and from Code Behind.
First example: A collection in a static property.
XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{x:Static local:MyValues.NameList}" />
<StackPanel Grid.Column="1">
<TextBox x:Name="tbNew"/>
<Button Content="Add" Click="Button_Click"/>
</StackPanel>
</Grid>
Code Behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
MyValues.NameList.Add(tbNew.Text);
}
Second example: a collection instance is created in the XAML resources.
XAML:
<Window.Resources>
<local:StringCollectionINCC x:Key="Strings">
<sys:String>Willam</sys:String>
<sys:String>Isak</sys:String>
<sys:String>Victor</sys:String>
<sys:String>Jules</sys:String>
</local:StringCollectionINCC>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Mode=OneWay, Source={StaticResource Strings}}" />
<StackPanel Grid.Column="1">
<TextBox x:Name="tbNew"/>
<Button Content="Add" Click="Button_Click"/>
</StackPanel>
</Grid>
Code Behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
StringCollectionINCC list = (StringCollectionINCC)Resources["Strings"];
list.Add(tbNew.Text);
}
Third example (best): creating collections in the MVVM pattern.
To create a team, an additional class is used that implements ICommand:
/// <summary>Executing Delegate.</summary>
/// <param name="parameter">Command parameter.</param>
public delegate void ExecuteHandler(object parameter);
/// <summary>CanExecuting Delegate.</summary>
/// <param name="parameter">Command parameter.</param>
/// <returns><see langword="true"/> - if command execution is allowed.</returns>
public delegate bool CanExecuteHandler(object parameter);
/// <summary>A class implementing the ICommand interface for creating WPF commands.</summary>
public class RelayCommand : ICommand
{
private readonly CanExecuteHandler _canExecute = CanExecuteDefault;
private readonly ExecuteHandler _execute;
private readonly EventHandler _requerySuggested;
public event EventHandler CanExecuteChanged;
/// <summary>The constructor of the command.</summary>
/// <param name="execute">Command Executable Method.</param>
/// <param name="canExecute">Team Status Method.</param>
public RelayCommand(ExecuteHandler execute, CanExecuteHandler canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
_requerySuggested = (o, e) => Invalidate();
CommandManager.RequerySuggested += _requerySuggested;
}
/// <summary>The method of invoking an event about a change in command status.</summary>
public void Invalidate() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public bool CanExecute(object parameter) => _canExecute == null ? true : _canExecute.Invoke(parameter);
public void Execute(object parameter) => _execute?.Invoke(parameter);
/// <summary>Default CanExecute Method/</summary>
/// <param name="parameter">>Command parameter.</param>
/// <returns>Is always <see langword="true"/>.</returns>
public static bool CanExecuteDefault(object parameter) => true;
}
ViewModel with collection and command:
/// <summary>ViewModel</summary>
public class MainVM
{
public ObservableCollection<string> NameList { get; }
= new ObservableCollection<string>()
{
"Willam",
"Isak",
"Victor",
"Jules"
};
private RelayCommand _addCommand;
public RelayCommand AddCommand => _addCommand
?? (_addCommand = new RelayCommand(AddMethod, AddCanMethod));
/// <summary>A method that checks that a parameter can be cast to
/// a string and that string is not empty.</summary>
/// <param name="parameter">Command parameter.</param>
/// <returns><see langword="true"/> - if the conditions are met.</returns>
private bool AddCanMethod(object parameter)
=> parameter is string val
&& !string.IsNullOrWhiteSpace(val);
/// <summary>Method to add a value to a collection.</summary>
/// <param name="parameter">Valid command parameter.</param>
private void AddMethod(object parameter)
=> NameList.Add((string) parameter);
}
Locator - A commonly used solution for accessing all ViewModel or other data containers:
/// <summary>Contains all ViewModel. In this case, only one MainVM.</summary>
public class Locator
{
public MainVM MainVM { get; } = new MainVM();
}
XAML App - here it is more convenient to create resources that may be needed in different Windows:
<Application.Resources>
<local:Locator x:Key="Locator"/>
</Application.Resources>
XAML Windows: setting ViewModel in a DataContext and binding properties of elements.
<Window ....
DataContext="{Binding MainVM, Mode=OneWay, Source={StaticResource Locator}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding NameList}" />
<StackPanel Grid.Column="1">
<TextBox x:Name="tbNew"/>
<Button Content="Add"
Command="{Binding AddCommand, Mode=OneWay}"
CommandParameter="{Binding Text, ElementName=tbNew}"/>
</StackPanel>
</Grid>
</Window>
If you need to use local:NameList then you have to do as the answer above. And if you need to use MyStringList property, then you have to do like below:
public partial class MainWindow : Window
{
public ObservableCollection<string> _myStringList=new ObservableCollection<string>(); // not initialized yet
public ObservableCollection<string> MyStringList
{
get
{
return _myStringList;
}
set
{
_myStringList = value;
}
}
public MainWindow()
{
InitializeComponent();
fillCombo();
DataContext = this;
}
public void fillCombo()
{
MyStringList.Add("Willam");
MyStringList.Add("Isak");
MyStringList.Add("Victor");
MyStringList.Add("Jules");
}
}
in XML:
<ComboBox ItemsSource="{Binding MyStringList}" Name="comboBox1" Margin="34,56,0,0"
VerticalAlignment="Top"
Width="200"/>
Note: you can also create a usercontrol or customcontrol in your project and then design the control with your desired element. After that you can use created control whichever XML pages you want.
There are several solutions, but to choose a specific one, you need to give more details of the task.
If you need a simple collection of strings, then you don’t need to create your own class.
Better to use, as #Clements wrote, StringCollection.
If you need the ability to change the collection (INotifyCollectionChanged interface), you can add your own class.
But you need to build it in the namespace, and not embed it in another class.
Initializing it with values ​​is better in XAML resources, rather than in C#-code.
namespace APX_Interface
{
public class StringCollectionINCC : ObservableCollection<string> { }
}
If there is a need to create a global instance accessible from anywhere in the application, then you can make a static property that provides this instance.
namespace APX_Interface
{
public static class MyValues
{
public static StringCollectionINCC NameList { get; }
= new StringCollectionINCC()
{
"Willam",
"Isak",
"Victor",
"Jules"
};
}
}
You can get it in XAML like this:
<Window.Resources>
<local:StringCollectionINCC x:Key="Strings">
<sys:String>Willam</sys:String>
<sys:String>Isak</sys:String>
<sys:String>Victor</sys:String>
<sys:String>Jules</sys:String>
</local:StringCollectionINCC>
<x:Static Member="local:MyValues.NameList" x:Key="Names"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Mode=OneWay, Source={StaticResource Strings}}" />
<ListBox Grid.Column="1" ItemsSource="{Binding Mode=OneWay, Source={StaticResource Names}}" />
</Grid>
You can also immediately, without resources, set a link to a static instance:
<ListBox Grid.Column="1" ItemsSource="{x:Static local:MyValues.NameList}" />
You have declared NameList as a nested class inside the MainWindow class. Move it out of there.
Besides that, you don't need to declare a MyStringList field in MainWindow, and NameList doesn't need to be an ObservableCollection, since you apparently never add or remove elements to/from the collection after it was initialized.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class NameList : List<String>
{
public NameList()
{
Add("Willam");
Add("Isak");
Add("Victor");
Add("Jules");
}
}
Now use it like this:
<Window.Resources>
<local:NameList x:Key="Names"/>
</Window.Resources>
...
<ComboBox ItemsSource="{StaticResource Names}" .../>
Instead of declaring a NameList class in code, you may as well directly declare a string list resource in XAML.
Add XAML namespace declarations like these:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:coll="clr-namespace:System.Collections.Specialized;assembly=System"
and this resource:
<coll:StringCollection x:Key="Names">
<sys:String>Willam</sys:String>
<sys:String>Isak</sys:String>
<sys:String>Victor</sys:String>
<sys:String>Jules</sys:String>
</coll:StringCollection>
EDIT: In case you want to be able to manipulate the string collection in code behind, declaring a string list resource is the wrong apprach.
Instead, declare a view model class with a NameList property
public class ViewModel : INotifyPropertyChanged
{
public ObservableCollection<string> NameList { get; }
= new ObservableCollection<string>();
}
and assign it to the DataContext of the MainWindow
private readonly ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewModel;
viewModel.NameList.Add("Willam");
viewModel.NameList.Add("Isak");
viewModel.NameList.Add("Victor");
viewModel.NameList.Add("Jules");
}
In XAML, bind the ComboBox like
<ComboBox ItemsSource="{Binding NameList}" .../>
EDIt2: Another, very simple approach may be a static member, e.g. in the MainWindow class, like
public partial class MainWindow : Window
{
...
public static List<string> NameList { get; } = new List<string>
{
"Willam", "Isak", "Victor", "Jules"
};
}
which would be used like this:
<ComboBox ItemsSource="{x:Static local:MainWindow.NameList}" .../>

How to bind an Enum from ViewModel to ComboBox - twoway

[Solved]: I have provided a working example in the answer below.
Related thread on [SO][1]
I have read many threads here, how to bind an enum to a combobox.
I got it to work oneway with another approach using a MarkupExtension!
But then I don't have twoway binding available.
I want to bind it to an Enum property of my ViewModel.
When I try this approach:
<Window.Resources>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="DetailScopeDataProvider">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:DetailScope" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
One further problem here is, that VS intellisense does not offer me this:
sys:Enum
I also don't know to setup the ItemSource and SelectedValue property of the combobox.
Ok, got it working.
The ObjectDataProvider has an ObjectType Property. As I want to use an Enum Property of my viewmodel as a datasource for the combobox.
The Object Type in this case is:
System.Enum
which resides in the System namespace in the mscorlib assembly.
We therefore have to add import the namespace in our XAML:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Here I will post the whole code for reference:
Test.xaml(window):
<Window x:Class="WpfTrash.Test"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfTrash"
-->> xmlns:sys="clr-namespace:System;assembly=mscorlib" <<--
mc:Ignorable="d"
Title="Test" Height="300" Width="300">
<Window.Resources>
<local:MyEnumToStringConverter x:Key="MyEnumConverter" />
<ObjectDataProvider x:Key="odp" MethodName="GetNames" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:TheEnum"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<StackPanel>
<TextBox x:Name="txtTheInt" Text="{Binding Path=TheInt}"/>
<TextBox x:Name="txtTheString" Text="{Binding Path=TheString}"/>
<!--<ComboBox x:Name="cboTheEnum" DataContext="{StaticResource SortedEnumView}" ItemsSource="{Binding}"/>-->
<ComboBox x:Name="cboTheEnum" ItemsSource="{Binding Source={StaticResource odp}}" SelectedValue="{Binding Path=TheEnum, Converter={StaticResource MyEnumConverter}}"/>
<Button x:Name="button" Content="Button" Click="button_Click"/>
</StackPanel>
</Window>
Test.xaml.cs
namespace WpfTrash
{
/// <summary>
/// Interaction logic for Test.xaml
/// </summary>
public partial class Test : Window
{
MyClass vm = new MyClass();
public Test()
{
InitializeComponent();
this.DataContext = vm;
}
private void button_Click(object sender, RoutedEventArgs e)
{
var selecteditem = (TheEnum)Enum.Parse(typeof(TheEnum), cboTheEnum.SelectedItem.ToString(), true);
}
}
}
The viewmodel:
public class MyClass : INotifyPropertyChanged
{
public int TheInt { get; set; }
public string TheString { get; set; }
TheEnum theEnum;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public TheEnum TheEnum
{
get
{
return theEnum;
}
set
{
theEnum = value;
NotifyPropertyChanged("TheEnum");
}
}
}
The Enum definition: (is not part of a class definition)
public enum TheEnum
{
A, B, C, D
}
The Converter:
public class MyEnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (TheEnum)Enum.Parse(typeof(TheEnum), value.ToString(), true);
}
}

Specify Binding of DependencyProperty in UserControl itself

Following up on my previous question (Change brushes based on ViewModel property)
In my UserControl I have have a DependencyObject. I want to bind that object to a property of my ViewModel. In this case a CarViewModel, property name is Status and returns an enum value.
public partial class CarView : UserControl
{
public CarStatus Status
{
get { return (CarStatus)GetValue(CarStatusProperty); }
set { SetValue(CarStatusProperty, value); }
}
public static readonly DependencyProperty CarStatusProperty =
DependencyProperty.Register("Status", typeof(CarStatus), typeof(CarView), new PropertyMetadata(OnStatusChanged));
private static void OnStatusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var control = (CarView)obj;
control.LoadThemeResources((CarStatus)e.NewValue == CarStatus.Sold);
}
public void LoadThemeResources(bool isSold)
{
// change some brushes
}
}
<UserControl x:Class="MySolution.Views.CarView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:views="clr-MySolution.Views"
mc:Ignorable="d"
views:CarView.Status="{Binding Status}">
<UserControl.Resources>
</UserControl.Resources>
<Grid>
<TextBlock Text="{Binding Brand}"FontSize="22" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<UserControl
Where do I need to specify this binding? In the root of the UserControl it gives an error:
The attachable property 'Status' was not found in type 'CarView'
In my MainWindow I bind the CarView using a ContentControl:
<ContentControl
Content="{Binding CurrentCar}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewmodel:CarViewModel}">
<views:CarView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
My ViewModel:
[ImplementPropertyChanged]
public class CarViewModel
{
public Car Car { get; private set; }
public CarStatus Status
{
get
{
if (_sold) return CarStatus.Sold;
return CarStatus.NotSold;
}
}
}
your binding isn't well written. instead of writing views:CarView.Status="{Binding Status}" you should write only Status="{Binding Status}"
It seems that your Control is binding to itself.
Status is looked for in CarView.
You should have a line of code in your control CodeBehind like :
this.DataContext = new ViewModelObjectWithStatusPropertyToBindFrom();
Regards

Passing parameter via Binding to UserConrol DependencyProperty

I have custom user control with the only property - SubHeader.
<UserControl x:Class="ExpensesManager.TopSection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<StackPanel>
<Label Name="Header" Content="Constant header text" Style="{StaticResource Header}"/>
<Label Name="SubHeader" Content="{Binding SubHeaderText}" Style="{StaticResource SubHeader}"/>
</StackPanel>
public partial class TopSection : UserControl
{
public TopSection()
{
this.InitializeComponent();
}
public static readonly DependencyProperty SubHeaderTextProperty =
DependencyProperty.Register("SubHeaderText", typeof(string), typeof(TopSection));
public string SubHeaderText
{
get { return (string)GetValue(SubHeaderTextProperty); }
set { SetValue(SubHeaderTextProperty, value); }
}
}
There are two usages in xaml. First with the constant text:
...
<my:TopSection SubHeaderText="Constant text"/>
...
Another one using binding:
<Page x:Class="MyNamespace.MyPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:my="clr-namespace:My"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
...
<my:TopSection SubHeaderText="{Binding MyModel.SubHeaderText}"/>
...
</Page>
My page code behind:
public partial class MyPage : Page
{
private MyModel myModel;
public MyModel MyModel
{
get
{
return this.myModel?? (this.myModel = new MyModel());
}
}
public MyPage(MyEntity entity)
{
this.InitializeComponent();
this.MyModel.MyEntity = entity;
}
}
MyModel code:
public class MyModel : NotificationObject
{
private MyEntity myEntity;
private string subHeaderText;
public MyEntity MyEntity
{
get
{
return this.myEntity;
}
set
{
if (this.myEntity!= value)
{
this.myEntity= value;
this.RaisePropertyChanged(() => this.MyEntity);
this.RaisePropertyChanged(() => this.SubHeaderText);
}
}
}
public string SubHeaderText
{
get
{
return string.Format("Name is {0}.", this.myEntity.Name);
}
}
}
The problem is that second one doesn't work. If I pass the constant text - it is displayed, if I use binding to the other property - nothing is displayed.
Does anybody knows what's wrong with the code? Thanks.
The problem is you set DataContext on the UserControl element. It will cause the following binding
<my:TopSection SubHeaderText="{Binding MyModel.SubHeaderText}"/>
to be relative to that DataContext, which is UserControl itself - so it cannot find the value.
To fix this, I suggest you not set DataContext on the UserControl, but the StackPanel inside:
<UserControl x:Class="ExpensesManager.TopSection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<StackPanel DataContext="{Binding RelativeSource={RelativeSource AncesterType=UserControl}}">
<Label Name="Header" Content="Constant header text" Style="{StaticResource Header}"/>
<Label Name="SubHeader" Content="{Binding SubHeaderText}" Style="{StaticResource SubHeader}"/>
</StackPanel>
Many people set DataContext on UserControl but that's really BAD. When you use the UserControl later, you have no idea the DataContext is actually set internally and will not respect the outside DataContext - really confusing. This rule also applies to other properties.
MyModel is a property in your DataContext? Try to check what object is your DataContext. If your data context is an object of your class MyModel you doesn't need the MyModel. part in your binding.
This kind of bindings always are to objects in your data context.
Hope this tips helps.
Declare your UserControl like this:
<my:TopSection
x:Name="myControl">
Then change your binding to this:
<my:TopSection SubHeaderText="{Binding MyModel.SubHeaderText, ElementName=myControl}"/>
You didn't set the Model in your UserControl
public partial class TopSection : UserControl
{
public class SampleViewModel { get; set; }
public TopSection()
{
this.InitializeComponent();
this.DataContext = new SampleViewModel();
}
public static readonly DependencyProperty SubHeaderTextProperty =
DependencyProperty.Register("SubHeaderText", typeof(string), typeof(TopSection));
public string SubHeaderText
{
get { return (string)GetValue(SubHeaderTextProperty); }
set { SetValue(SubHeaderTextProperty, value); }
}
}
Update
Since you don't want Model to known to the View. Create a ViewModel
public class SampleViewModel : NotificationObject
{
public class MyModel { get; set; }
public class SampleViewModel()
{
MyModel = new MyModel() { SubHeaderText = "Sample" };
RaisePropertyChange("MyModel");
}
}

Categories