I have a combo box that I type my Database Server names in it. I just want it to remeber what I have typed so far so next time it adds it to a list of items in it so I don't have to type the same Database Server name again each time I run the app.
As for saving the names I have typed in the combox, I am fine with saving them in a file, like a text file, Json, XML, whatever.
I don't know how to do the binding? and when to load the file? Can you help me with an example?
<ComboBox x:Name="serverTxt" Height="23" VerticalAlignment="Top" Text="{Binding Path=ServerNames}"/>
Here's some code that came from this answer, with a little updating and added the storage/retrieval. It should get you started at least. Note that this solution requires a second element on your window (I added a second combobox here) because it triggers on LostFocus, otherwise it will update for each character as you type.
Set up your xaml like this:
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="149,43,0,0" VerticalAlignment="Top" Width="120" IsEditable="True"
ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" Text="{Binding NewItem, UpdateSourceTrigger=LostFocus}"/>
<ComboBox x:Name="comboBox1" HorizontalAlignment="Left" Margin="349,122,0,0" VerticalAlignment="Top" Width="120"/>
Then your main window:
public partial class MainWindow : Window
{
private string _selectedItem;
private ObservableCollection<string> ServerNames;
private string fileLocation = #"C:\Temp\ServerNames.txt";
public MainWindow()
{
ServerNames = new ObservableCollection<string>();
if (File.Exists(fileLocation))
{
var list = File.ReadAllLines(fileLocation).ToList();
list.ForEach(ServerNames.Add);
}
DataContext = this;
InitializeComponent();
}
public IEnumerable Items => ServerNames;
public string SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
public string NewItem
{
set
{
if (SelectedItem != null)
{
return;
}
if (!string.IsNullOrEmpty(value))
{
ServerNames.Add(value);
SelectedItem = value;
}
}
}
protected void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private void Window_Closing(object sender, CancelEventArgs e)
{
if (!File.Exists(fileLocation))
{
File.Create(fileLocation);
}
File.WriteAllLines(fileLocation, ServerNames);
}
}
Related
I have a ListView that is bound on an ObservableCollection.
<ListView Grid.Column="0" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="0" Margin="5" Name="CustomerListView" ItemsSource="{Binding Customers}" SelectedItem="{Binding Path=CurrentCustomer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Margin="5,0,0,0" Text="{Binding LastName}"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the same View i have some TextBoxes which are meant to edit the CurrentCustomer. I also have a save button. If you click this button the modifications of the CurrentCustomer should be saved. If the button "cancel" is pressed the modifications should be discarded.
<TextBox Name="CustomerSalutationTextBox" Grid.Column="1" Grid.Row="0" Height="20px" Margin="5" Text="{Binding Path=CurrentCustomer.Salutation, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The Problem is, if i make some changes on the currentCusomer, they are taking effect immediately.
Do you have a solution?
What you need to add in your ViewModel / the class you have a binding context to is to save what was previous in the Textfield.
And when you hit abort, u just overwrite your newValue with the old one.
I'm going to setup a small example.
class ExampleViewModel : INotifyPropertyChanged {
private string _customerLastName;
private string _customerName;
private string _initialCustomerName;
private string _initialCustomerLastName;
public string CustomerName {
get { return this._customerName; }
set {
this._customerName = value;
this.OnPropertyChanged();
}
}
public string CustomerLastName {
get { return this._customerLastName; }
set {
this._customerLastName = value;
this.OnPropertyChanged();
}
}
public ExampleViewModel(string customerName, string customerLastName) {
this.CustomerName = customerName;
this.CustomerLastName = customerLastName;
this._initialCustomerName = customerName;
this._initialCustomerLastName = customerLastName;
}
//example event handler for your abort button
private void OnAbortButtonClick(object sender, EventArgs args) {
this.CustomerName = this._initialCustomerName; //set the initial name
this.CustomerLastName = this._initialCustomerLastName; //set the initial lastName
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Alternative
As you might load your data from a database/csv file/something else, you should know the original values. When pressing the cancel button, you could invoke a CancelButtonClicked event in your ViewModel and some other class which subscribed to the ViewModels event and knows the original Model could set the original values on that viewModel instance, or just exchange the ViewModel instance with the original one.
Have a look at : https://msdn.microsoft.com/en-us/library/hh848246.aspx
class ExampleViewModel : INotifyPropertyChanged {
private string _customerLastName;
private string _customerName;
public event CancelButtonClicked CancelButtonClicked;
public string CustomerName {
get { return this._customerName; }
set {
this._customerName = value;
this.OnPropertyChanged();
}
}
public string CustomerLastName {
get { return this._customerLastName; }
set {
this._customerLastName = value;
this.OnPropertyChanged();
}
}
public ExampleViewModel(string customerName, string customerLastName) {
this.CustomerName = customerName;
this.CustomerLastName = customerLastName;
}
private void OnAbortButtonClick(object sender, EventArgs args) {
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
internal delegate void CancelButtonClicked(object sender);
public class SomeOtherClass {
private ExampleViewModel _viewModel;
public SomeOtherClass() {
this._viewModel = new ExampleViewModel("foo", "bar");
this._viewModel.CancelButtonClicked += ViewModelOnCancelButtonClicked;
}
private void ViewModelOnCancelButtonClicked(object sender) {
ExampleViewModel vm = sender as ExampleViewModel;
vm.CustomerName = "foo"; //set the initial values again
vm.CustomerLastName = "bar";
}
}
Alternative2
You could also exchange the complete VM when the event of the cancel button is invoked to retreive its original state.
Alternative3
Everytime your SelectedItem changes, you could save the current state of it by creating a copy of it. When your CancelButton is pressed, you set the SelectedItem to the copy of your original viewModel.
You'd need a copy constructor or a copy method for that purpose.
I've found out another solution. In the code behind of the view i've added following:
void saveButton_Click(object sender, RoutedEventArgs e)
{
BindingExpression be = customerFirstNameTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
My textbox with UpdateSourceTrigger Explicit
<TextBox Name="customerFirstNameTextBox" Grid.Column="1" Grid.Row="2" Height="20px" Margin="5" Text="{Binding Path=CurrentCustomer.FirstName, Mode=TwoWay, UpdateSourceTrigger=Explicit}" IsEnabled="{Binding Path=IsCustomerTextEnabled}"/>
And my button
<Button Name="SaveButton" Click="saveButton_Click" Margin="5" Content="Save"/>
I have a simple ComboBox which looks like following:
<ComboBox ItemsSource="{DynamicResource ItemsCompColl}"
TextSearch.TextPath="ItemName"
SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True">
........................
</ComboBox>
It works well. Now, I am using the ItemId property which is bound in SelectedValue to check if user has selected an appropriate item from comboBox or not.
Issues:
When user selects a value from ComboBox, the ItemId property is set to the Id of the Selected Item in ComboBox. After that if user goes to next Control and returns to ComboBox and enters some garbage value to ComboBox, the ItemId of ComboBox does not change, I mean it's not reset to "0". So, my validation fails and user succeeds in entering the garbage values.
OK, so you want to Set SelectedValue to 0 when there is any validation error in the editable TextBox of theComboBox. You need to check the validation result of the Text and then reset your SelectedValue to 0 if the validation fails.
Here is an working example for you:
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<ComboBox ItemsSource="{Binding ComboboxItems}"
IsEditable="True" DisplayMemberPath="ItemName"
Text="{Binding SelectedName, ValidatesOnDataErrors=True}"
SelectedValue="{Binding SelectedID, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True">
</ComboBox>
<TextBox Text="{Binding SelectedID,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</Window>
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
MyViewModel mvm;
public MainWindow()
{
InitializeComponent();
mvm = new MyViewModel()
{
ComboboxItems = new ObservableCollection<ComboItem>()
{
new ComboItem{ItemName="item1",ItemId=1},
new ComboItem{ItemName="item2",ItemId=2},
new ComboItem{ItemName="item3",ItemId=3}
},
};
this.DataContext = mvm;
}
}
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
protected void RaisePropertyChanged(String propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
public class ComboItem : ObservableObject
{
private string _itemname;
private int _itemid;
public string ItemName
{
get
{
return _itemname;
}
set
{
_itemname = value;
RaisePropertyChanged("ItemName");
}
}
public int ItemId
{
get { return _itemid; }
set
{
_itemid = value;
RaisePropertyChanged("ItemId");
}
}
}
public class MyViewModel : ObservableObject, IDataErrorInfo
{
private int _selectedid;
private string _selectedname;
public ObservableCollection<ComboItem> ComboboxItems
{
get;
set;
}
public int SelectedID
{
get { return _selectedid; }
set
{
if (_selectedid != value)
{
_selectedid = value;
RaisePropertyChanged("SelectedID");
}
}
}
public string SelectedName
{
get { return _selectedname; }
set
{
if (_selectedname != value)
{
_selectedname = value;
RaisePropertyChanged("SelectedName");
}
}
}
public string Error
{
get { return this[SelectedName]; }
}
public string this[string columnName]
{
get {
switch (columnName)
{
case "SelectedName":
{
if (SelectedName!=null && ComboboxItems.Count(x => x.ItemName == SelectedName) == 0)
{
//reset selected value to 0
this.SelectedID = 0;
return "Invalid selection";
}
break;
}
}
return null;
}
}
}
}
Result:
When user enter valid text (e.g. item1), the Textbox below shows the correct ItemId of the SelectedValue, and when user enter invalid text, the selected value will be reset to 0.
P.S: When garbage is entered in ComboBox, it will always display validation error indicator (red border as shown above), and if you data-bind SelectedItem to a property, it will be null. So you shouldn't care about the SelectedValue if there is an error, that's why I was saying I can't reproduce the error in the comments.
You really got me with this, i've never realized this problem existed. I've found a solution that works it you don't care to clear the combo on focus. There are probably better ways, but none i can think about. Maybe someone out there has another solution.
First of all, add a reference to Windows.System.Interactivity in your proyect, and add this to your XAML:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Then, add this code to the combobox:
<ComboBox ItemsSource="{DynamicResource ItemsCompColl}"
TextSearch.TextPath="ItemName" x:Name="cbItems"
SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotMouseCapture">
<i:InvokeCommandAction Command="{Binding ClearCombo}"
CommandParameter="{Binding ElementName=cbItems}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Finally, let's create the command in ouw View Model:
RelayCommand<System.Windows.Controls.ComboBox> _clearCombo;
public ICommand ClearCombo
{
get
{
if (_clearCombo == null)
{
_clearCombo = new RelayCommand<System.Windows.Controls.ComboBox>(this.ClearComboCommandExecuted,
param => this.ClearComboCommandCanExecute());
}
return _clearCombo;
}
}
private bool ClearComboCommandCanExecute()
{
return true;
}
private void ClearComboCommandExecuted(System.Windows.Controls.ComboBox cb)
{
cb.Text = "";
}
Hope this helps with your problem.
Edit
Ok, after #XAMlMAX comment, I think he is right and this may be done in Code Behind easily and probably it's better in MVVM pattern. Simply add a event handler to the combobox to capture GotMouseCapture:
<ComboBox ItemsSource="{DynamicResource ItemsCompColl}"
TextSearch.TextPath="ItemName" x:Name="cbItems"
SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True"
GotMouseCapture="cbItems_GotMouseCapture" >
And then in code behind of the View:
private void cbItems_GotMouseCapture(object sender, MouseEventArgs e)
{
((ComboBox)sender).Text = "";
}
Edit 2
Well, one final, ugly idea to solve it. I don't like it at all, but maybe it solves your problem.
First of all, you must subscribe to the TextBoxBase.TextChanged event:
<ComboBox ItemsSource="{DynamicResource ItemsCompColl}"
TextSearch.TextPath="ItemName" x:Name="cbItems"
SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
Grid.IsSharedSizeScope="True"
TextBoxBase.TextChanged="cbItems_TextChanged" >
Then in code behind add this code:
private void cbItems_TextChanged(object sender, TextChangedEventArgs e)
{
string text = ((ComboBox)sender).Text;
((YourViewModel)this.DataContext).ItemId= text;
}
This way, you make sure any time the ComboBox changes its text, you get notified about it. It's really horrible code, but i've runned out of ideas...
Please look at the image below:
Only three items in the listbox are displayed in the above image but it can be any number of items depending on the user's choice.
Now, as you can see in the image above each item has two comboboxes. Now I want to have selectedItem or SelectedValue in my viewModel from which I should be able to get the user's selection. Now I don't know how to bind these comboboxes for getting the user's selection.
Suppose I have only one item instead of the list then I would declare a property of type int so that I can easily get the selectedValue but for the list I am very much confused. Can anybody point me to the right direction?
To start of, lets say the class you are going to be binding the combo box is
public class UnitSource :INotifyPropertyChanged
{
public IEnumerable Units
{
get { return new[] { "Test Unit", "Alternate Unit" }; }
}
string _selectedComboItem1;
public string SelectedComboItem1
{
get
{
return _selectedComboItem1;
}
set
{
if (_selectedComboItem1 == value)
return;
_selectedComboItem1 = value;
OnPropertyChanged();
}
}
string _selectedComboItem2;
public string SelectedComboItem2
{
get
{
return _selectedComboItem2;
}
set
{
if (_selectedComboItem2 == value)
return;
_selectedComboItem2 = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Then in your view model you will have an ObservableCollection of the UnitSource Like below
public ObservableCollection<UnitSource> MuchoUnitSources
{
get; set;
}
To get the selected ListBoxItem have this in your ViewModel
private UnitSource _selectedUnitSource;
public UnitSource SelectedUnitSource
{
get
{
return _selectedUnitSource;
}
set
{
if (_selectedUnitSource == value)
return;
_selectedUnitSource = value;
OnPropertyChanged();
}
}
Lets assume it is initialized like so
MuchoUnitSources = new ObservableCollection<UnitSource>(new []{ new UnitSource(),new UnitSource() });
The in your view your listbox should look like below
<ListBox Name ="TestList1" ItemsSource="{Binding MuchoUnitSources}" SelectedItem="{Binding SelectedUnitSource}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<ComboBox SelectedItem="{Binding SelectedComboItem1}" ItemsSource="{Binding Units}" />
<ComboBox SelectedItem="{Binding SelectedComboItem2}" ItemsSource="{Binding Units}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now whenever you select an item from any of the combobox they will update the objectbeing bound to.
I currently have a Combo Box which is populated with Artist names and I need to bind it to another once the Artist is selected. These are set up as follows in my view:
<ComboBox Height="23" HorizontalAlignment="Left" Margin="65,81,0,0" Name="comboBox1" ItemsSource="{Binding Artists}" SelectedItem="{Binding SelectedArtist}" VerticalAlignment="Top" Width="120" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="65,115,0,0" Name="comboBox2" VerticalAlignment="Top" ItemsSource="{Binding Albums}" SelectedItem="{Binding SelectedAlbums}" Width="120" />
In my ViewModel I have the following:
private void initialiseArtists()
{
MusicDataClassesDataContext dataClasses = new MusicDataClassesDataContext();
artistList = (from m in dataClasses.tblArtists select m.ArtistName).ToList();
}
public List<String> Artists
{
get
{
return this.artistList;
}
}
public string SelectedArtist
{
set
{
this.selectedArtist = value;
initialiseAlbums();
}
}
private void initialiseAlbums()
{
if (selectedArtist != null)
{
MusicDataClassesDataContext dataClasses = new MusicDataClassesDataContext();
var getArtist = dataClasses.tblArtists.FirstOrDefault(band => band.ArtistName == selectedArtist);
albumList = (from album in dataClasses.tblAlbums
where album.ArtistID == getArtist.ArtistID
select album.AlbumName).ToList();
//dataClasses.tblAlbums.SelectMany(album => album.ArtistID == getArtist.ArtistID).ToList();
}
}
public List<String> Albums
{
set
{
initialiseAlbums();
}
get
{
return this.albumList;
}
}
I was assuming that the Selected Item event would populate the second Combo Box but I was mistaken.
You have to notify the view that the Albums property changed.
That's why you have to implement the INotifyPropertyChanged interface and call the PropertyChanged event after building your albums list.
For example
public class Musics : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void initialiseAlbums()
{
if (selectedArtist != null)
{
//Your code
OnPropertyChanged("Albums");
}
}
}
Its good workaround to use ObservableCollection<> instead of List<>.
I was assuming that the Selected Item event would populate the second
Combo Box but I was mistaken
there is no selecteditem event. what you have is a SelectedItem Binding. and if you want your view to react to an action in your viewmodel you have to raise INotifyPropertyChanged.
so after your initialiseAlbums(); you have to call OnPropertyChanged("Albums"); at least.
ps: you can also create parentchildrelationobjects, makes such stuff more easier.
public class MyArtist
{
public string Name{get;set;}
public IList<Album> Albums {get;set}
}
Change it to SelectedItem="{Binding SelectedArtist, Mode=TwoWay}".
Edit:
And add a getter to the SelectedArtist property.
I have MainWindow containing a datagrid and a "filter panel". The filter panel can change by a user input(button click). I try to achieve it with databinding. The problem that Im facing is the filter panel(which is a user control) is not loaded or refreshed.
Mainwindow xaml:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250*" />
<ColumnDefinition Width="253*" />
</Grid.ColumnDefinitions>
<DataGrid AutoGenerateColumns="True" Height="200" HorizontalAlignment="Left" Margin="23,28,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="200" ItemsSource="{Binding OverviewableItems}" />
<UserControl Grid.Column="1" Content="{Binding UserControl}" DataContext="{Binding}" Grid.ColumnSpan="2" />
<Button Content="PersonFilter" Height="23" HorizontalAlignment="Left" Margin="23,268,0,0" Name="buttonPersonFilter" VerticalAlignment="Top" Width="75" Click="buttonPersonFilter_Click" />
<Button Content="ProjectFilter" Height="23" HorizontalAlignment="Left" Margin="132,268,0,0" Name="buttonProjectFilter" VerticalAlignment="Top" Width="75" Click="buttonProjectFilter_Click" />
</Grid>
code behind:
private ViewModel _viewModel;
public MainWindow()
{
_viewModel = new ViewModel(new DataProvider());
DataContext = _viewModel;
_viewModel.PropertyChanged += _viewModel.SetFilterType;
InitializeComponent();
}
private void buttonProjectFilter_Click(object sender, RoutedEventArgs e)
{
_viewModel.OverviewType = OverviewType.Project;
}
private void buttonPersonFilter_Click(object sender, RoutedEventArgs e)
{
_viewModel.OverviewType = OverviewType.Person;
}
First user control:
<Grid>
<DatePicker Grid.Column="1" Grid.Row="1" Height="25" HorizontalAlignment="Left" Margin="19,18,0,0" Name="datePickerFundingTo" VerticalAlignment="Top" Width="115" Text="{Binding ElementName=ProjectFilter, Path=FundingTo}" />
</Grid>
code behind for this user control is only this:
public DateTime FundingTo { get; set; }
public ProjectFilter()
{
FundingTo = DateTime.Now;
InitializeComponent();
}
Other user control: just simply contains a TextBox and a Button, for the sake of simplicity I didnt add any code behind to it.
ViewModel of the MainWindow:
public class ViewModel : INotifyPropertyChanged
{
private UserControl _userControl;
public UserControl UserControl
{
get { return _userControl; }
set
{
if (_userControl == value)
{
return;
}
OnPropertyChanged("UserControl");
_userControl = value;
}
}
private OverviewType _overviewType = OverviewType.None;
public OverviewType OverviewType
{
get { return _overviewType; }
set
{
if (_overviewType == value)
{
return;
}
OnPropertyChanged("OverviewType");
_overviewType = value;
}
}
private ObservableCollection<IOverviewItem> _overviewableItems;
public ObservableCollection<IOverviewItem> OverviewableItems
{
get { return _overviewableItems; }
set
{
if (_overviewableItems == value)
{
return;
}
_overviewableItems = value;
}
}
private readonly DataProvider _dataProvider;
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel(DataProvider dataProvider)
{
_dataProvider = dataProvider;
}
public void SetFilterType(object sender, EventArgs eventArgs)
{
switch (_overviewType)
{
case OverviewType.Project:
_userControl = new ProjectFilter();
break;
case OverviewType.Person:
_userControl = new PersonFilter();
break;
}
}
public void OnPropertyChanged(string name)
{
if (PropertyChanged == null)
return;
var eventArgs = new PropertyChangedEventArgs(name);
PropertyChanged(this, eventArgs);
}
}
plus I have an enum OverviewType with None,Project,Person values.
The property changed event fired properly, but the user control is not refreshed. Could anyone enlight me, where is the flaw in my solution?
And the other question I have, how can I communicate from the usercontrols to the mainwindow viewmodel? Forex: the datagrid should be changed according to its filter.
Any help would be greatly appreciated. Cheers!
There are different problems here.
As Clemens said, you must fire your event after the value is updated. But it's not the main issue here.
Second problem: you are affecting your new usercontrol to the private member, so you're totally bypassing your property.
Replace
_userControl = new ProjectFilter();
by
this.UserControl = new ProjectFilter();
Third problem, which is not directly related to your question but actually is your biggest problem: you have an architecture design issue. You're exposing in your viewmodel a UserControl, which is an anti-pattern. Your viewmodel must not know anything about the view, so it must NOT have any reference to the controls inside the view. Instead of the binding you wrote, you could fire an event from the viewmodel and add an event handler in your view so it's your view that updates the usercontrol.
Try to fire the PropertyChanged after changing a property's backing field:
public UserControl UserControl
{
get { return _userControl; }
set
{
if (_userControl != value)
{
_userControl = value; // first
OnPropertyChanged("UserControl"); // second
}
}
}
Similar for OverviewType.