The WPF piece of code is quite simple:
<telerik:RadGridView Name="AnalisiKey"
AutoGenerateColumns="True"
Margin="10,273,694,59"
d:DataContext="{d:DesignInstance Type=viewModels:FrequentKeywordFinderViewModel, IsDesignTimeCreatable=True}"
ItemsSource="{Binding ItemCollectionViewSourceSingole}"
ClipboardCopyMode="All" SelectionMode="Extended" SelectionUnit="Mixed">
<!--<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn x:Name="Keyword" Header="Keyword" Language="it-it" DataMemberBinding="{Binding (viewModels:KeyFreq.Keyword)}" />
<telerik:GridViewDataColumn x:Name="FreqNelDocum" Header="FreqNelDocum" Language="it-it" UniqueName="FreqNelDocum"/>
</telerik:RadGridView.Columns>-->
</telerik:RadGridView>
As well as the ViewModel
class FrequentKeywordFinderViewModel : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public List<KeyFreq> ItemCollectionViewSourceSingole { get; set; } = new List<KeyFreq>();
}
And the piece of code where the ItemSource is populated:
private void MostroRisultatiSuGriglia(List<KeyFreq> singole,
List<KeyFreq> doppie, bool excludeUnfrequentKeys)
{
var dataContext = ((FrequentKeywordFinderViewModel)this.DataContext);
var itemCollectionViewSourceSingole = dataContext.ItemCollectionViewSourceSingole;
singole = CalcolaTfIdf(StopWordsUtil.FrequenzaKeywords, singole);
dataContext.ItemCollectionViewSourceSingole.AddRange(singole.Where(s => s.FreqNelDocum > 1).ToList());
itemCollectionViewSourceDoppie.Source = doppie.Where(s => s.FreqNelDocum > 1).ToList();
}
With Snoop I can delve into the datagrid.ItemSource and see the items. But they don't appear in the datagrid. Any suggestion?
A key point to be aware of when using binding is that the control doesn't get updated from bound properties unless and until it's notified that the values have changed. There are two basic ways to implement this notification:
Inherit your ViewModel from INotifyPropertyChanged and invoke the PropertyChanged event whenever your property value changes. This approach is suitable for most situations, including numerical and string properties bound to controls such as TextBlock and TextBox.
Use ObservableCollection for collections bound to the ItemsSource property (for controls which have an ItemsSource property).
Controls are aware of the INotifyPropertyChanged interface and the INotifyCollectionChanged interface underlying ObservableCollection, and listen for the appropriate PropertyChanged and CollectionChanged events.
Guidelines for selecting the appropriate technique are as follows:
If the property value in the ViewModel is set before the control's DataContext has been set to the ViewModel and never subsequently changes, you actually don't need to use the PropertyChanged notification at all, because the control will see the intended property value when the ViewModel is bound.
If you are binding to a property for which the value will be intially assigned or will change after the DataContext has been set to the ViewModel, the ViewModel must inherit from INotifyPropertyChanged and the property setter must invoke the PropertyChanged event, otherwise the control will never be aware that the property value has changed.
If you are binding a collection to control's ItemsSource property, you need to consider the above, but you also need to consider how and when you are populating or updating the collection's contents.
If you are creating and populating a collection such as a list, then setting a ViewModel's property (which is bound to a control's ItemsSource property) and never modifying the collection's contents (although you may later assign the ViewModel property to different collection), the ViewModel must inherit from INotifyPropertyChanged and the collection property setter must invoke the PropertyChanged event. In this scenario, you actually don't need to consider ObservableCollection; you can use any desired collection type in your ViewModel.
If you are modifying your collection's contents while it is bound to a control's ItemsSource property, CollectionChanged events are required for the control to update correctly; the easiest way to accomplish this is to use an ObservableCollection in your ViewModel; it will automatically raise CollectionChanged events as items are added or removed.
These are basic guidelines that should help to identify and resolve the most common/most likely problems when using binding.
Typical property binding:
public class MyViewModel : INotifyPropertyChanged
{
private string _myString;
public string MyString
{
get => _myString;
set
{
_myString = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyString));
}
}
}
In your case, you might only need to change ItemCollectionViewSourceSingole from List<KeyFreq> to ObservableCollection<KeyFreq> because you initialize the empty collection in the ViewModel constructor and only add items later.
Related
I've got a dilemma regarding the DataContext. Let's inspect the following piece of XAML:
<Window xmlns:my="clr-namespace:MyNamespace.Controls"
... >
...
<my:MyControl Name="{Binding Prop1}" Value="{Binding Prop2}" />
</Window>
Obviously, the Window's code-behind contains something like:
DataContext = someViewModel;
Author's intentions are clear - he wants to bind MyControl's Name and Value to Window's DataContext's Prop1 and Prop2. And this will of course work. Unless. (dramatic pause)
Unless MyControl is a composite UserControl, which also wants to take advantage of short notation of bindings and sets its DataContext to its own viewmodel. Because then it will become clear, that the bindings in Window's XAML actually bind to MyControl's DataContext (previously inherited from Window's one) and now they will stop working (or worse, will keep working if MyControl's viewmodel actually contains properties named Prop1 and Prop21).
In this particular case solution is to bind in Window's code explicitly:
<Window x:Name="rootControl"
xmlns:my="clr-namespace:MyNamespace.Controls"
... >
...
<my:MyControl Name="{Binding ElementName=rootControl, Path=DataContext.Prop1}"
Value="{Binding ElementName=rootControl, Path=DataContext.Prop2}" />
</Window>
TL;DR If we're using short notation of bindings (when binding to DataContext) we may encounter quite tough to nail bugs resulting from bindings suddenly pointing to wrong DataContext.
My question is: how to use short binding notation without risk, that I'll bind to wrong DataContext? Of course I may use the short notation when I'm sure, that I'll be using inherited DataContext and long notation when I'm sure, that control will modify its DataContext. But that "I'm sure" will work only until first mistake, which will consume another hour of debugging.
Maybe I'm not following some MVVM rule? E.g. for example DataContext should be set only once on the top level and all composited controls should bind to something else?
1 I've gone through that, actually. The Window's DataContext contained a property named (say) Prop and the control replaced its DataContext with a class, which also contained a property Prop and everything worked fine. Problem appeared when I tried to use (unconsciously) the same pattern with non-matching property names.
By request:
Fragment of MyControl's code:
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
// Using a DependencyProperty as the backing store for Name. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(MyControl), new PropertyMetadata(null));
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(MyControl), new PropertyMetadata(0));
Window's viewmodel:
public class WindowViewmodel : INotifyPropertyChanged
{
// (...)
public string Prop1
{
get
{
return prop1;
}
set
{
prop1 = value;
OnPropertyChanged("Prop1");
}
}
public int Prop2
{
get
{
return prop2;
}
set
{
prop2 = value;
OnPropertyChanged("Prop2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Now assume, that on changing of Name and Value dependency properties, MyControl generates some viewmodel and executes the code:
model = new MyControlViewModel(Name, Value);
this.DataContext = model;
And internal MyControl controls bind to this DataContext.
From now on, the original Name and Value bindings will no longer work.
Unless MyControl is a composite UserControl, which also wants to take advantage of short notation of bindings and sets its DataContext to its own viewmodel.
And that's where I stopped reading. This is, imho, a MVVM anti-pattern.
The reason for this is twofold. First, you screw with anybody who is using the control. "Hey," you say, "you can't bind your stinky VM to my beautiful UI. You have to use MY custom VM!" But what if your VM is hard to use, lacks logic or features needed by the overall application? What happens when, to use your UI, we have to translate our VM/models back and forth with your VM? Pain in the butt.
Second is that your custom control is UI. Its logic is UI logic, and so it is unnecessary to use a view model. It is better to expose DependencyProperties on your control and update your UI as necessary. That way anybody can bind to your UI and use it with any model or view model.
You can solve your problems by simply not using what you call a 'composite control. While I understand that you want to encapsulate some functionality in the associated view model, you don't need to set the view model to the UserControl.DataContext internally.
What I mean by this is that you can have a view model for any or all of your UserControls, but they're data classes, not UI classes, so keep them out of the view code. If you use this method of adding DataTemplates into Resources, then you won't need to set any DataContext properties at all:
<DataTemplate DataType="{x:Type ViewModels:YourUserControlViewModel}">
<Views:YourUserControl />
</DataTemplate>
The final difference is that you should add your view model for your UserControls as properties in a parent view model. This way, you still have no duplicated code (except maybe just a property declaration) and more importantly, you have no Binding problems from mixing DataContext values.
UPDATE >>>
When using this DataTemplate method of hooking up views and view models, you can display your view by Binding your view model property to the Content property of a ContentControl like this:
<ContentControl Content="{Binding YourViewModelProperty}" />
At run time, this ContentControl will be rendered as whatever view or UserControl that you defined in the DataTemplate of the relevant type for that property. Note that you shouldn't set the x:Key of the DataTemplate, otherwise you'd also need to set the ContentControl.ContentTemplate property and that can limit the possibilities afforded by this method.
For example, without setting the x:Key property on your DataTemplates, you could have a property of a base type and by setting it to different sub class, you can have different views for each from the one ContentControl. That is the basis of all of my views... I have one property of a base class view model data bound like this example and to change views, I just change the property to a new view model that is derived from the base class.
UPDATE 2 >>>
Here's the thing... you shouldn't have any 'proxy' object in your UserControls doing anything... it should all be done through properties. So just declare a DependencyProperty of the type of that object and supply it from the view model through data Binding. Doing it this way means that it will be easy to test the functionality of that class, whereas testing code behind views is not.
And finally, yes, it's perfectly fine doing this in MVVM:
<Controls:SomeUserControl DataContext="{Binding SomeViewModelProperty}" />
The overriding goal of MVVM is just to provide separation between the UI code and the view model code, so that we can easily test what's in the view models. That is why we try to remove as much functionality code from the views as possible.
within a usercontrol you should never set the datacontext to "this" or a new viewmodel. a developer/user of your MyUsercontrol expect that the datacontext inherit from top to bottom (from mainwindow to your myusercontrol).
your usercontrol xaml should use element binding
MyUserControl.xaml
<UserControl x:Name="uc">
<TextBlock Text="{Binding ElementName=uc, Path=Name}"/>
<TextBlock Text="{Binding ElementName=uc, Path=Value}"/>
this means your following code will work now in every situation
<Window xmlns:my="clr-namespace:MyNamespace.Controls">
<my:MyControl Name="{Binding Prop1}" Value="{Binding Prop2}" />
</Window>
the property Prop1 from Datacontext mainwindow is bound to the DP Name from your MyUsercontrol and the Textblock.Text within your MyUsercontrol is bound to the DP Name.
I've never met such a problem. It seems to be a little bit theoretical to me but maybe because of my approach to working with DataContext in WPF.
I minimize the explicit use DataContext property. I set it manually only for windows.
I have one dedicated method which is responsible for displaying new windows and it is the only one place where the DataContext property is set explicitly.
DataContext property for Windows is set to root ViewModel which contains child ViewModels, which contain...
I allow WPF to select which View should be used to display given a ViewModel by using DataTemplate
In my application I have a single ResourceDictionary which contains mappings between all ViewModels and Views.
I know you can create custom controls and dependency property for wpf controls like expained here http://msdn.microsoft.com/en-us/library/ms753358.aspx, I want to know if you can create custom dependency property in the same way for devExpress Controls ? and how ?
There is no way to bind multiple items in comboxBoxEdit control. I want to create a dependency property called SelectedItems on ComboBoxEdit.
I already created a custom property on normal ComboBox called SelectedEnumeration which binds directy to the enums and gets the value. No need to use ObjectDataProvider.
There is no way to bind multiple items in comboxBoxEdit control.
Wrong. Check DevExpress.Xpf.Editors.CheckedComboBoxStyleSettings
Basically, you can bind ComboBoxEdit.EditValue to a collection, which gets populated with the selected items.
<dxe:ComboBoxEdit ItemsSource="{Binding MyItems}"
EditValue="{Binding SelectedItems}">
<dxe:ComboBoxEdit.StyleSettings>
<dxe:CheckedComboBoxStyleSettings />
</dxe:ComboBoxEdit.StyleSettings>
</dxe:ComboBoxEdit>
ViewModel:
public class SomeViewModel
{
public ObservableCollection<MyClass> MyItems {get;set;}
public ObservableCollection<MyClass> SelectedItems {get;set;}
}
I already created a custom property on normal ComboBox called
SelectedEnumeration which binds directy to the enums and gets the
value. No need to use ObjectDataProvider.
You're putting too much responsibility on the UI, where it does not belong. Create a proper ViewModel and have your data processed by the ViewModel in such a way that it facilitates regular DataBinding to the UI. Don't resort to reflection and other types of uneeded hacks in order to put logic in the wrong layer.
I'm curious how this works, because I have a MainViewModel, which has Property say called SubViewModel which has a Property of ObservableCollection (we'll call it Property1.)
I've implemented INotifyChangedProperty on everything.
My Main Window
<Window ..
DataContext="{Binding MainViewModel}" />
...
<StackPanel DataContext="{Binding SubViewModel}">
<local:SomeControl DataContext="{Binding}" />
</StackPanel>
</Window>
And my UserControl
<UserControl Name="SomeControl">
<DataGrid Name="MyDataGrid" ItemSource="{Binding Property1, Mode=TwoWay}" CurrentCellChanged="TestMethod" />
...
</UserControl>
In my test method, just as a test to figure out why the changes are not propegating up to the main view model I do something like this
private void TestMethod()
{
var vm = this.DataContext as SubViewModel;
var itemSourceObservableCollection = MyDataGrid.ItemsSource as ObservableCollection<MyType>;
//I thought vm.Property1 would be equal to itemSourceObservableCollection
//but they are not, itemSourceObservableCollection shows the changes I've made
//vm.Property1 has not reflected any changes made, even though I though they were the same item
}
So I figured out that ItemSource must create a copy of the item you bind it to? I'm stuck here, how do manually notify the viewModel that this property has changed and it needs to update? I thought that was INotifyPropertyChanged's job?
I think part of my problem is I lack the understanding of how this kinda works internally. If anyone can point to a good blog post, or documentation to help me understand why my code isn't working the way I expected, that would be great.
1) No copy is made.
2) ObservableCollection will propogate changes made to the collection, not the items within the collection. So you'll see additions, deletions etc. but NOT property changes to items within the collection.
3) If you want to see changes made to individual items in the ObservableCollection, you need to implement INotifyPropertyChanged on those items.
There's actually TWO different issues here. What happens internally when you bind to a collection? AND why changes on the user surface are not propagated back to your View Model. Based upon what you wrote, the two issues are not connected, but let's take them one at a time...
For the first issue... When you bind a collection, the WPF binding engine creates a "CollectionView" class that mediates between your object store and the logical tree. You can, if needed, get a copy of the the "CollectionView" using a static method on CollectionViewSource...
var cvs = CollectionViewSource.GetDefaultView(MyCollectionOfThings);
There are several interesting properties in the result, and some of them contain write accessors which allow you to directory modify the CollectionView.
For the second issue... The business classes in your SubViewModel need to inherit from INotifyPropertyChanged such that changes are 'announced' via the WPF binding engine. Your VM should be a publisher, but can also be a subscriber. A property that participates in the INotifyPropertyChanged plumbing gets declared like this...
private string _name;
[Description("Name of the driver")]
public string Name
{
[DebuggerStepThrough]
get { return _name; }
[DebuggerStepThrough]
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
This code publishes changes, but can also subscribe to changes made on the user surface by setting the appropriate attributes in your Xaml.
Background reading: What is a CollectionView?
Also, Similar question
I have a ComboBox in WPF binding its ItemsSource Property to a Property returning an IEnumerable of String. The binding is just one-way. The class that contains the data for the ComboBox implements INotifyPropertyChanged Interface and calls the OnPropertyChanged(..) as soon as the Property gets updated. When I leave the ComboBox untouched the changes are correctly propagated. But as soon as the ComboBox is expanded once or a value is selected the changes in the ItemsSource Collection are no longer updated. What may be the reason for this behaviour?
Heres the XAML
<ComboBox Name="cmbSourceNames"
Grid.Row="0"
SelectedItem="{Binding Path=CurrentSource, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=SourceAddresses, NotifyOnSourceUpdated=True}"/>
The DataContext is set in the code behind:
this.cmbSourceNames.DataContext = this._dpa;
And this one is the Method that triggers the change of the Property. The Method for adding the Packet is delegated to the Current Dispatcher with BeginInvoke.
private void DispatcherAddDataPacket(DataPacket dp)
{
ObservableCollection<DataPacket> dpList;
this._dpkts.TryGetValue(dp.SourceAddress, out dpList);
if (dpList == null)
{
dpList = new ObservableCollection<DataPacket>();
dpList.Add(dp);
this._dpkts.Add(dp.SourceAddress, dpList);
OnPropertyChanged("SourceAddresses");
}
else
{
dpList.Add(dp);
}
}
The Property is giving back the Keys of the Dictionary as IEnumerable.
Finally I implemented the Binding Property using an ObservableCollection tracking the keys when a new Packet gets added (so every key to the Dictionary has an equivalent in this ObservableCollection). I think it's not really a satisfying solution (because you have to keep track of both Collections independently) but it works like this.
I am binding a DataGrid to an ObservableCollection as:
<DataGrid ItemsSource="{Binding Path=MyCollection, Mode=TwoWay}">
The property in the viewmodel looks like:
public ObservableCollection<MyType> MyCollection
{
get { return this.Model.Collection; }
set { /* Empty but needed for data binding */ }
}
The set accessor for MyCollection is never called or used but is required by the TwoWay DataBinding. The contained Model class initializes Collection at construction because it is required to place the object in a valid state. Is there anyway to get around this unnecessary accessor or are there any alternatives to this?
Set the Mode=OneWay and you don't need a set.
If the two-way binding is being used both ways, then it is being used, just not explicitly by you in code. As far as I know, you can't get around declaring it. The same with declaring any read-write property. For whatever reason, you have to write them out as
Public Type Property
{
get;
set;
}
MS didn't want to make it TOO easy for you. =)
OneWay is concerned with Getter and OneWayToSource is Setter. Of course, TwoWay is concerned with Getter and Setter.
At the point of view, how can you change ItemsSource of DataGrid in View like Text of TextBox? I think it is impossible, so you don't need to set TwoWay binding. Change to OneWay and remove the setter.