Sorting on Templated Column with converters - wpf

My datamodel is like this:
public class ModelA
{
public int ModelId{get;set;}
}
public class ModelB
{
public IEnumerable<ModelA> ChildObjects{get;set;}
}
Now in the Xaml, am using a DataGrid with the ItemSource as List(), and have a template column which binds to ChildObjects with a converter doing the job of getting the first element from ChildObjects and returning the value as that object's ModelId. Now all works fine till now. The issue is when I do sorting on this templated column.
I know one workaround is to have an extra property in ModelB which does the job of what converter is doing and make the sortmemberpath in xaml as that new property name, but that is not what I want as its against the model.
Is there any other perfect way to handle this scenario, as the SortMemberPath can't be made as expression as its just a contant.

You've tagged this MVVM, which I assume means your models are actually view models (or are at least wrapped by view models). That being the case, why wouldn't you add the extra property? After all, it's there to support the view. Your view needs the extra property, so your view model should provide it.

Related

WPF MVVM: View's ListBox with source deep in Model. How to implement?

I'm new to WPF. I need to Bind UI's ListBox to the source that is deep in Model Layer.
App scheme is on picture below. Desc:
My MainWindowViewModel Class has a Scheduler Property (Scheduler Class in Model layer).
Scheduler Class has a CurrentParser Property (Parser Class in Model layer).
Parser Class has a Result field (ParserResultMetaData Class in Model layer).
ParserResultMetaData Class has a Log field (Log is a List(Of String))
Log can be changed only programmatically from model layer (Parser adds lines during it's work).
So my question is how can I bind my ListBox to this List to match MVVM pattern.
As I get it now, ViewModel must have an ObservableCollection(Of String) witch is a copy of my List(Of String) from Model layer.
Somehow you need to notify the UI when a line is added to the collection. There are multiple ways to achieve this, but if the collection is modified from within the model layer, you need a mechanism for communicating this to other layers in one way or another.
Use an ObservableCollection in your Model layer.
While types like ObservableCollection and INotifyPropertyChanged are widely used in MVVM architectures, they are not specific to them and in some cases it can make sense to use them in the model layer directly. Using an ObservableCollection in your Parser class is one way to provide this notification mechanism. You can then bind the ItemsSource of the ListBox to Scheduler.Parser.Result.Log directly and it will update accordingly.
Create a wrapper property in your ViewModel.
If you don't want to use an ObservableCollection in your model, you can expose the List via a property in your ViewModel, for example:
public IEnumerable<string> ParserLog
{
get { return Scheduler.Parser.Result.Log; }
}
Then you need to manually notify the UI when an item is added, so you're gonna need an event (or something equivalent) which tells your ViewModel that the list changed and it needs to raise the PropertyChanged Event for the ParserLog property. Add code like this in your ViewModel:
this.Scheduler.Parser.ResultUpdated += (s, e) => this.RaisePropertyChanged("ParserLog");
This will tell the ListBox to update the items from the ParserLog property.

WPF DataTemplate / DataTemplateSelector -- Best approach for a ViewModel used by 2 different Views?

Basically, I have the following scenario:
ViewModel: FooViewModel : BaseViewModel, BarViewModel : BaseViewModel
Views: MainView, FooView, BarView
Right now I "inject" the view and set the DataContext using DataTemplate and DataTemplateSelector. Obviously, my ItemsControl ItemSource is bound to an ObservableCollection<BaseViewModel> in which it contains (for now) an instance of a FooViewModel and a BarViewModel
The problem is I want to introduce a AlternateFooView which I want to utilize the same FooViewModel. I figure I'll create another DataTemplate and have my DataTemplateSelector return it, but there needs to be special logic to determine which DataTemplate to return (I can't just go by which ViewModel is there), and that means I'll have to have some type of property or field in the BaseViewModel. I don't know if that's really a good idea because that seems to be introducing a field/property into the ViewModel that is only used to select a view. It won't hurt my unit testing, but it seems like a waste to include a field just to help decide which UI View to choose. I don't think it breaks MVVM, but I'm curious if anyone out there has any other better ideas? The alternative ideas I had I dislike even more...
Idea #2:
- Turn FooViewModel into a base class that 2 different FooViewModel's extend (i.e. BaseFooViewModel, FooViewModel, DifferentFooViewModel). This seems stupid because there really isn't any difference between FooViewModel and DifferentFooViewModel aside from their class type.
Idea #3:
- Just copy FooViewModel and make it FooViewModel2 (it'll be exactly identical to FooViewModel). This seems even worse than idea #2.
Sample-Code (Adjusted obviously):
public abstract class BaseViewModel : NotificationObject
{
//Common Stuff
}
public abstract MainViewModel : NotificationObject
{
public MainViewModel()
{
MyItems = new ObservableCollection<BaseViewModel>()
{
new FooViewModel();
new BarViewModel();
new FooViewModel(); //New Item -- I want it to use the DifferentFooView
}
//Load items from a DAL later
}
public ObservableCollection<BaseViewModel> MyItems { get; set; }
//Other Stuff
}
<l:MyItemsControl ItemSource={Binding MyItems} ContentTemplateSelector={StaticResource MyTemplateSelector} />
Thanks!
I agree with krishnaaditya that the question really boils down to what determines which View to use based on the state of the ViewModel. This type of logic is often placed into a template selector, which works great. If you don't want to put that logic into the template selector, consider externalizing it by using my Routed Template Selection approach. That makes it easy to delegate the template selection logic by using a routed event.
The idea you proposed in your comment about using a DataTrigger could also work, but that reintroduces the need for a property on the VM object that indicates which View to load (which you said you don't want). In my opinion, that's not necessarily a bad thing.

Can I have a ValueConverter in my ViewModel?

I have a combobox bound to a collection, so the user can select one of the items. So far, so good.
The content of the combo box is driven by the item, but also by a value in my viewmodel. Imagine the value in my viewmodel is the language, I have dictionary of descriptions by language in my bound item, and I want to display the correct one.
How should I go about this?
This is a classic example of why the ViewModel exists - you want to have logic which depends on trivial state in the view, as well as the main model.
Imagine you are writing a unit test to run against the ViewModel for this behaviour. You would need the ViewModel to have a property mapped to the selected item. The ViewModel would also have another property which varies according to this selected item as well as the other value in the ViewModel you mentioned.
I think of this as the test-driven approach to ViewModel design - if you can't write a unit test to evaluate it then you haven't got the mix of state and published interfaces right.
So, yes, the ViewModel can solve the problem and if you push all the state down into it you can do the unification within the ViewModel.
Make an observable collection in your viewmodel of type Item. Bind the itemsource of your viewmodel to this observable collection.
public class Item
{
public String description {get;set;}
public String language {get;set;}
public override ToString()
{
return description;
}
}
Selected item would also be bound to a property of type Item as well.
The override of ToString displays the description.
The Selected item propery will have a reference to the selected object property where you can get the language from.

WPF: Binding DataGrid to a list<Product> having a DataGridComboBoxColumn bound to a list<Category>?

I have a DataGrid with ItemsSource set to a list of products and
I have a DataGridComboBoxColumn inside the DataGrid with ItemsSource set to a list of categories. That way I want the user to choose a certain category for each product.
I always get the binding error:
BindingExpression path error: 'Categories' property not found on 'object' ''Product' (Hash)
Well I do not want to make the Category list part of the Product entity as 1:N relation, although it would work that way.
I want to keep them separate.
Anyone knows a workaround?
Create class with static property like
static class ValueLists
{
public static IEnumerable<Category> Categories {get {... }}
}
and use following binding
ItemsSource="{x:Static myNs:ValueList.Categories}" />
this is kind of late reply but in order to share the knowledge I found this:
Binding a WPF DataGridComboBoxColumn with MVVM
This answer shows that is not always mandatory to convert the second list to a static class, you can always specify a RelativeSource and search for an specific Ancestor and then bind to the "other" list you have in your ViewModel.
This is probably relevant to your problem.
What is happening here?
The Columns collection is just a property in the Datagrid; this collection is not in the logical (or visual) tree, therefore the DataContext is not being inherited, which leads to there being nothing to bind to.

WPF Exposing a calculated property for binding (as DependencyProperty)

I have a complex WPF control that for some reasons (ie. performance) is not using dependency properties but simple C# properties (at least at the top level these are exposed as properties).
The goal is to make it possible to bind to some of those top level properties -> I guess I should declare them as DPs.(right? or is there some other way to achieve this? )
I started reading on MSDN about DependencyProperties and DependencyObjects and found an example:
public class MyStateControl : ButtonBase
{
public MyStateControl() : base() { }
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
"State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));
}
If I'm right - this code enforces the property to be backed up by DependencyProperty which restricts it to be a simple property with a store(from functional point of view, not technically) instead of being able to calculate the property value each time getter is called and setting other properties/fields each time setter is called.
What can I do about that? Is there any way I could make those two worlds meet at some point?
//edit
I guess I have to tell you a little more about what I want to do and what my limitations are. So:
I have TabControl that is bound to a collection of ViewModel(I'm using MVVM pattern) objects. Every tab is meant to be an editor for one object of that collection. Objects can be of different types so I have multiple definitions each with a different DataType property. Now I have that complex WPF Control that I want to use as a part of one of those DataTemplates. If I use usual TextBox I can simply bind to its Text property, but I can't do the same with Text property of that custom control simply because its Text property is not a dependency property.
In this scenario I have :
no direct access to the control itself nor to its events
no code behind file that I can use to do that kind of thing
I can see however a dirty solution -
In the Window class I would have to subscribe to CollectionChanged event of the collection that is bound to the TabControl.
Whenever an item is added to that collection use ItemContainerGenerator to obtain a copy of I suppose TabItem and use it to find the right copy of 'complex control'
Regiter items handlers to 'complex controls' events to do the job
This is wrong because:
this is agains MVVM - I have to play with tab control to do the job instead of doing it in the ViewModel class
this couples in an unwanted way the view and viewmodel
I think you are mixing up Dependency Properties and implementing INotifyPropertyChanged on your classes.
You don't need your property to be a dependency property, you just need your class to implement INotifyPropertyChanged and call OnPropertyChanged whenever the state of your object changes in a way that would affect the value you want to expose to binding.
So let's say you have a property Sum that you want to bind to. The Sum property simple adds two other properties (or fields, or whatever) together. When anything happens that affects the Sum calculation, you want to notify that the Sum value has changed, so the any controls bound to Sum get updated.
public int Sum => Value1 + Value2;
public int Value1
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value1 = value;
OnPropertyChanged("Sum");
}
}
public int Value2
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value2 = value;
OnPropertyChanged("Sum");
}
}
It seems to me that you've been saddled with a WPF user control that was built by someone who didn't intend it to be used with data binding. I would assume that this is for one of two reasons: a) there's some logical reason that you shouldn't be able to bind to this property, or b) the original author of this control didn't know what he was doing. You haven't provided enough information for me to know which of those two conditions is the one you're really working under.
But in general, the reason you can't expose calculated properties for binding is that calculated properties generally don't have a setter. It doesn't make sense to set a property whose value is calculated.
If there are other properties whose values need to be updated when this one changes, the right approach (or at least the one consonant with the design of dependency properties) is to handle those updates in the dependency property's callback function, which is kind of what the callback function is for.

Resources