I have a ComboBox whose ItemSource collection can be changed from other part of application and this ComboboBox can be used to multiple places of app. Thus, to centralize this I have created a user control that contains only combobox and set it datasource to viewmodel and has exposed a dependency property for binding. But the dependency property doesn't updated bound property on combobox item selection.
Here are my extracted sample codes
//My base Model for Combobox data
public class MyModel
{
public int Id { get; set; }
public string Text { get; set; }
}
//Base class for View Models to notify property change
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) =>{};
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
//View model for user control that contains combobox
public class MyModelViewModel : BaseViewModel
{
private ObservableCollection<MyModel> _mymodelCollection;
public ObservableCollection<MyModel> MyModelCollection
{
get { return _mymodelCollection; }
set
{
_mymodelCollection = value;OnPropertyChanged();
}
}
public MyModelViewModel()
{
MyModelCollection = new ObservableCollection<MyModel>
{
new MyModel
{
Id = 1, Text = "Project 1"
},
new MyModel
{
Id = 2, Text = "Project 2"
},
new MyModel
{
Id = 3, Text = "Project 3"
}
};
}
}
//View model for parent window that contains combobox user control
public class TestWindowViewModel : BaseViewModel
{
private int _myModelId;
public int MyModelIdInWindow
{
get { return _myModelId; }
set {
_myModelId = value; OnPropertyChanged();
//debug code
Console.WriteLine(value);
}
}
}
//User Control for reusability
<UserControl x:Class="SimpleWPF.Control.CommonControl"
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:local="clr-namespace:SimpleWPF.Control"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<ComboBox
x:Name="cmbList"
Grid.Row="0"
Grid.Column="0"
ItemsSource="{Binding MyModelCollection, Mode=OneWay}"
DisplayMemberPath="Text"
SelectedValuePath="Id"
SelectionChanged="cmbList_SelectionChanged"
/>
</Grid>
</UserControl>
//User Control Code Behind with exposed MyModelId DP
public partial class CommonControl : UserControl
{
private bool _isInternalUpdate = false;
public int MyModelId
{
get { return (int)GetValue(MyModelIdProperty); }
set { SetValue(MyModelIdProperty, value); }
}
// Using a DependencyProperty as the backing store for BranchId. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyModelIdProperty =
DependencyProperty.Register("MyModelId", typeof(int), typeof(CommonControl),
new FrameworkPropertyMetadata(int.MinValue,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, MyModelIdPropertyChanged,
null, false, UpdateSourceTrigger.PropertyChanged));
public CommonControl()
{
InitializeComponent();
this.DataContext = new MyModelViewModel();
}
private void cmbList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(cmbList.SelectedValue != null)
{
_isInternalUpdate = true;
MyModelId = (int)cmbList.SelectedValue;
_isInternalUpdate = false;
}
}
private static void MyModelIdPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CommonControl branchComboBox)
{
branchComboBox.UpdateBranch();
}
}
private void UpdateBranch()
{
if (!_isInternalUpdate)
{
cmbList.SelectedValue = MyModelId;
}
}
}
Finally in the window
<ctrl:CommonControl MyModelId="{Binding MyModelIdInWindow, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
MyModelIdInWindow property doesn't get updated even though in user control MyModelId DP value gets changed