WPF MVVM Where do you put UI Functions that wouldn't appear on your ViewModel - wpf

My understanding is that when you take your View and say:
myView.DataContext = myViewModel;
You are kind of assigning the class that it should refer to, almost like the code-behind in a most apps. I've always loved the design but where is the best place to put display type logic that really doesn't belong in your view model? For example say you're modifying a context menu for an item depending on the item's status. In the past I've handled different bits of display functionality with converters. I was going to use the Views native code behind but then I realized I don't think I have access to that do I?

Typically you would have a model object for each item in your list. Then wrap each model in a view model. The view model would then expose properties for the attributes you described (colors, fonts, etc.) and fire property change notifications. Hope that helps.

My assumption that setting the DataContext for a View was the same as pointing it to a different code behind file was incorrect. The DataContext is used for binding purposes. You can still reference methods in the normal code behind like so:
<CheckBox Margin="5,0,30,0"
x:Name="OSHPD" IsChecked="{Binding OSHPD}"
Validation.ErrorTemplate="{x:Null}" Checked="OSHPD_Checked" Unchecked="OSHPD_Unchecked">OSHPD Approval</CheckBox>
private void OSHPD_Checked(object sender, RoutedEventArgs e)
{
FM.IsEnabled = false;
}
private void OSHPD_Unchecked(object sender, RoutedEventArgs e)
{
FM.IsEnabled = true;
}
Databinding IsChecked="{Binding OSHPD}" is hitting the ViewModel while the events Checked="OSHPD_Checked" Unchecked="OSHPD_Unchecked" reference the Views code behind.

Most things that go in a View's code behind could also go in a ValueConverter, Behavior or AttachedProperty. Each of these will be able to access Control level properties. Generally you are able to supply these with values like the colours/brushes/shapes and other view specific data, to keep the valueConverter/behaviour/AttachedProperty generic if you did find you needed to reuse it.

Related

MVVM design choice regarding responsibility of ViewModel for simple states

I've recently started to delve into MVVM architectural pattern. I've understood large parts of it but still few doubts remain regarding how much responsibility ViewModel should take on behalf of View.
In other words, how dumb should View be?
For example, for simple state coordination like clearing TextView after user presses SubmitButton. This kind of state coordination requires no more than one-liner to implement using some of the popular data-binding frameworks.
For example in pseudo-ReactiveCocoa:
textView.text <~ submitButton.pressed.map { _ in "" }
However, Martin Fowler wrote in his Presentation Model,
The Presentation Model contains the logic that says that the composer field is only enabled if the check box is checked, so the when the view updates itself from the Presentation Model, the composer field control changes its enablement state
which suggests that even the simple logic like clearing out TextView after pressing Button should be encapsulated inside ViewModel.
Such design choice leads to something like this (again, in pseudo-ReactiveCocoa):
// In View
viewModel.submitButtonPressed <~ submitButton.pressed
textView.text <~ viewModel.textViewText
// In ViewModel
textViewText <~ viewModel.submitButtonPressed.map { _ in "" }
Although it leads to better encapsulation of logics while assuming view with the job of binding only (making it dumb), it does make code a lot more verbose and lead to tighter coupling between View and ViewModel (by requiring ViewModel to be aware of SubmitButton).
I'm still new to MVVM pattern in general and learning stuff every day.
How much responsibility should ViewModel take?
In other words, must View be completely dumb and only handle simple binding (connect its UI elements to bindable property provided by ViewModel) or is it okay for View to handle fairly simple logic like the above?
On a side note, is tight coupling between View and ViewModel okay in MVVM?
In general, the ViewModel takes all responsibility. More specifically, in your scenario, the ViewModel wouldn't know about the submitButton, but rather the View would know that the ViewModel exposes a command (an ICommand) called SubmitCommand, and have the submitButton bind to that command.
Sometimes it can get a bit more involved to completely separate the actions and corresponding logic, for instance when there's no binding available for a command for a specific event. But in those cases a fairly simple attached behavior (i.e. InvokeCommandAction and friends, see the documentation) can bridge that gap to coax flow so the logic can go in to the ViewModel.
Very rarely, there are scenarios (of which none come to mind currently) where it gets so involved that I just skip the whole idea, and separate as much as possible, rather than to have to work out three months later exactly what the hell is going on. But those cases are rare indeed.
In other words, must View be completely dumb and only handle simple binding
It's quite good, when view contains data bindings only, but IRL complex views can contain some view-specific logic. E.g., since single view model could be connected to a several views, focus management is a view's prerogative. Another sample is a logic like "hide element A if element B is disabled", or "change color from A to B if button is checked", etc.
XAML frameworks provide several techniques to make view logic more well-composed: commands, triggers, attached behaviors, value converters. But sometimes you actually need code-behind.
For example, for simple state coordination like clearing TextView
after user presses SubmitButton
To be more clear. This is not a view logic, and must be placed in view model:
public class ViewModel
{
private string someText;
public string SomeText
{
get { return someText; }
set
{
if (someText != value)
{
someText = value;
OnPropertyChanged();
}
}
}
private ICommand submitCommand;
public ICommand SumbitCommand
{
if (submitCommand == null)
{
submitCommand = new RelayCommand(() =>
{
// do submit
// clear text
SomeProperty = null;
});
}
return submitCommand;
}
}
XAML:
<TextBox x:Name="SomeTextBox" Text="{Binding SomeText}"/>
<Button Content="Submit" Command="{Binding SubmitCommand}">
But this is a view logic:
public MyWindow()
{
InitializeComponent();
// SomeTextBox should have initial focus
Loaded += (sender, args) => SomeTextBox.Focus();
}
is tight coupling between View and ViewModel okay in MVVM?
Ideally all components should be loosely coupled, but view must know about view model properties to perform data binding.

Own property of UserControl in WPF

I have a user control with 2 grids on it. Now I want to be able to retrieve the grid that has the focus and expose it to my view model. How can I do this in WPF?
I want to fill a property in my view model with the name of the Grid that has focus. It seems not to be easy.
Can anyone help?
Thx!
You really should reconsider your design if you are exposing UI elements or specific parts to your viewmodel. Usually your viewmodel should not know of any specific ui element. What exactly do you want to do with the name of the ui element? You could listen to a GotFocus event on your two grids
like
<Grid x:Name="Grid1" GotFocus="OnGridGotFocus"/>
<Grid x:Name="Grid2" GotFocus="OnGridGotFocus"/>
and add this method to your UserControl, in this method you could retrieve it via
private static void OnGridGotFocus(object aSender, RoutedEventArgs aE)
{
string name = (string)(aSender as DependencyObject).GetValue(NameProperty);
}
the name could now be written into a DependencyProperty which you bind to your view model. But again, i still think you should not do this.
If you explain what exactly you are trying to achieve, maybe we can help you better.

MVVM Focus To Textbox

How would I set focus to a TextBox without specifying the name for that TextBox? At the moment I am doing the following
<Window FocusManager.FocusedElement="{Binding ElementName=Username}">
<Grid>
<TextBox Text="{Binding Username}" Name="Username" />
</Grid>
</Window>
Is there any way of doing this without specifying a Name for the TextBox. As I believe in MVVM having a Name element usually means bad design?
As I believe in MVVM having a Name element usually means bad design?
No, it’s not.
The MVVM pattern is not about eliminating all the code from code-behind files.
It is about separating of concerns and increasing the testability.
View related code like focus handling should remain in the code-behind file of the View. But it would be bad to see application logic or database connection management in the code-behind file of the View.
MVVM examples with code in the code-behind files without violating the MVVM pattern can be found at the WPF Application Framework (WAF) project.
The simple way is to set focus in UserControl_Load event
this.txtBox.Focus();
txtBox.Focusable = true;
Keyboard.Focus(txtBox);
MVVM doesn't mean you can not put code in the code behind file.
In fact, Do not let any pattern restrict you to find the best way of coding.
I have documented a "pure MVVM" way to do this in my answer to a similar problem. The solution involves using Attached Properties and a framework for passing interface commands from the ViewModel back to the View.
Code behind should be avoided when possible, even more when it is in the view. I had the same problem and for simple purposes the best answer is this one as it only modifies the view:
WPF MVVM Default Focus on Textbox and selectAll
If you are looking to set again focus as you interact with other UserControl elements, this will do the trick:
Set focus on textbox in WPF from view model (C#)
I lost 3 days figuring this out, I hope this can help.
As I believe in MVVM having a Name element usually means bad design?
No, it’s not.
According to Microsoft MVP's not only is naming controls is WPF bad practice, it is a quite substantial hit on performance. Just wanted to pass along some words of wisdom
I agree with Sean Du about not letting any pattern totally restrict you, I think performance hit should be avoided whenever possible.
Actually, I found the boolean attached property solution a bit dirty and clumsy in the way that you have to find a twist in order to be sure that the next set of your view model property will really raise the attached property changed event.
A simple and more elegant solution is to bind your behavior on property type for which you can be sure that the next value will always be different from the previous one and thus be sure that your attached property changed event will raise every times.
The most simple type that comes into mind is the int. The solution is then the usual combination of :
The behavior:
public static class TextBoxFocusBehavior
{
public static int GetKeepFocus(DependencyObject obj)
{
return (int)obj.GetValue(KeepFocusProperty);
}
public static void SetKeepFocus(DependencyObject obj, int value)
{
obj.SetValue(KeepFocusProperty, value);
}
// Using a DependencyProperty as the backing store for KeepFocus. This enables animation, styling, binding, etc...
public static readonly DependencyProperty KeepFocusProperty =
DependencyProperty.RegisterAttached("KeepFocus", typeof(int), typeof(TextBoxFocusBehavior), new UIPropertyMetadata(0, OnKeepFocusChanged));
private static void OnKeepFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBox t = d as TextBox;
if (t != null)
{
t.Focus();
}
}
}
The view model property:
public int InputFocus
{
get { return _inputFocus; }
private set
{
_inputFocus = value;
Notify(Npcea.InputFocus);
}
}
The use of the attached behavior:
<TextBox v:TextBoxFocusBehavior.KeepFocus="{Binding InputFocus}"/>
And finaly the use of the property in the VM:
public void YouMethod()
{
//some code logic
InputFocus++;//<= the textbox focus
}
Some REALLY bad minded spirits might say that this logic is bound to the int32 size limitation. Well... I will just choose to ignore them right now ;-)

Accessing the WebContext class in XAML

The generated App.xaml.cs file contains this
private void Application_Startup(object sender, StartupEventArgs e)
{
// This will enable you to bind controls in XAML files to WebContext.Current
// properties
this.Resources.Add("WebContext", WebContext.Current);
but I cannot figure out how to refer to this object. It seems like I ought to set the page DataContext = "WebContext" and then use it like this
... property="{Binding Path=User.IsAuthenticated}
I'm sure it's straightforward. Can someone clue me in?
Well done, and of course I can't believe I didn't see that. With the addition of a value converter suddenly a tangled mess of state management code becomes declarative, showing and hiding various UI elements according to whether the user is logged in or in various roles without me having to litter my code with trivial conditionals at myriad logical inflection points.
Try:-
property="{Binding Path=User.IsAuthenticated, Source={StaticResource WebContext}}"
that ought to find it. Its not an object you'd want directly assigned to the DataContext normally, the similarity of the names is co-incidental.

MVVM CollectionViews in WPF Application Framework (WAF)

In short my question is: How do you prefer to expose filtered/sorted/grouped ObservableCollections to Views in WAF?
I was fairly happy with my first attempt which involved filtering on the VM and exposing an ICollectionView of Model objects for the View to bind to:
public StartDetailViewModel(IStartDetailView view, StartPoint start, Scenario scenario)
: base(view)
{
this.scenario = scenario;
this.start = start;
this.startsViewSource = new CollectionViewSource();
this.startsViewSource.Filter += new FilterEventHandler(Starts_Filter);
this.startsViewSource.Source = scenario.Starts;
}
public ICollectionView FilteredStarts
{
get
{
return startsViewSource.View;
}
}
void Starts_Filter(object sender, FilterEventArgs e)
{
if (e.Item != null)
{
e.Accepted = (((StartPoint)e.Item).Date == this.start);
}
}
}
However, exposing the Model objects directly is insufficient since each item now needs its own ViewModel.
So, CollectionViewSource.Source is now attached to a collection of Views. The main problem with this is when applying filters:
void Starts_Filter(object sender, FilterEventArgs e)
{
//Since e.Item is now a view we are forced to ask the View for the ViewModel:
StartItemViewModel vm = ((IStartItemView)e.Item).GetViewModel<StartItemViewModel>();
[...]
}
This feels wrong to me. Are there better approaches?
UPDATE
So I reverted to a CollectionViewSource.Source of Model objects and maintained a seperate collection of child View objects to which the View was bound.
The question then of course is why am I using CollectionViewSource in a ViewModel at all?
I think the following prinicple applies: If the filtering/sorting functionality is a property of the View only (i.e. an alternate view might legitimately not provide such functionality) then CollectionViews should be used in the View (with code-behind as necessary). If the filtering/sorting functionality is a dimension of the Model then this can be dealt with in the ViewModel or Model by other means.
This makes sense once you realise that code-behind in MVVM views is perfectly acceptable.
Any comments?
I think the real benefit of CollectionView lies in when you are in need of reporting information as you step through collectionview items one by one. In this way you are able to utilize the CurrentPosition property and MoveCurrentToNext (/etc.) methods which may be desireable. I particularly like the idea of being able to report PropertyChanged notifications in MVVM when item properties in the collection changed/items are added/removed/changed.
I think it just makes a bit more sense to use in controls that require more complex notifications (such as datagrid, where you may want to raise PropertyChanged events and save to your datastore each time the selectionchanges or a new item is addd to the control).
I hope that makes sense. That is just what I am putting together as a beginner.
Also, I really don't think anything should go in the code-behind of a view except a datacontext and the shared data you may be feeding it from a viewmodel.

Resources