MVVM Class Instantiation and Args - wpf

I'm trying to whip my brain into MVVM mode here.
I have a Grid in my view, and various methods for manipulating it and adding shapes to it in my viewmodel. I realize that I should try to avoid that sort of thing in the VM, so I moved those methods into there own class. What I'm trying to figure out is
a) Where should an instance of the new class be created? Currently I've got it in my VM, using
IoC.Get()
b) The NewClass needs to get a hold of the Grid in the view, how can I do that?
(The only thing I can think of is to have the VM get a reference to its View, and pass the Grid
into the NewClass, but that doesn't seem like the best way to do it)

A) look at using Coroutines if you need to manipulate the grid in a way you're not able to through the viewmodel. A reference to the view gets passed in the ActionExecutionContext.
public interface IResult
{
void Execute(ActionExecutionContext context);
event EventHandler<ResultCompletionEventArgs> Completed;
}
public class ActionExecutionContext
{
public ActionMessage Message;
public FrameworkElement Source;
public object EventArgs;
public object Target;
public DependencyObject View;
public MethodInfo Method;
public Func<bool> CanExecute;
public object this[string key];
}

Related

Trouble passing parameters in Prism-App (WPF)

In my prism application (WPF) I have a view model that passes parameters to another view model, let's call this MyViewModel. I have implemented the the BindableBase class and the INavigationAware interface. So my view model looks like this (simplified for the purpose of this question).
class MyViewModel : BindableBase, INavigationAware
{
private IRegionManager _regionManager;
private ObservableCollection<MyClass> _myClassCollection;
private string _myParameter;
private NavigationParameters _navigationParameters = new NavigationParameters();
public ObservableCollection<MyClass> MyClassCollection => _myClassCollection;
public MyViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
_myClassCollection = GetMyClassData(_myParameter);
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
_myParameter = navigationContext.Parameters.GetValue<string>("MyPassedParameter");
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return false;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
}
I can see that the MyPassedParameter gets passed as expected in OnNavigatedTo method. The issue is, that the extract of the data - GetMyClassData - to be displayed in the corresponding view to this view model is depending on this parameter. Since the contructor runs before OnNavigatedTo, _myParameter has value null when I am using it in the constructor.
Can anybody tell me how I can use the passed parameter in the data extract for the view model.
Thanks in advance.
If you intend to set _myClassCollection to GetMyClassData(_myParameter), it makes no sense to do this before you have gotten the _myParameter so you should move your initialization logic to the OnNavigatedTo method:
public MyViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
_myParameter = navigationContext.Parameters.GetValue<string>("MyPassedParameter");
_myClassCollection = GetMyClassData(_myParameter);
}
_myParameter is a navigation parameter that is not present by the time the view model is constructed.
Since the contructor runs before OnNavigatedTo, _myParameter has value null when I am using it in the constructor.
This is a fundamental limitation of Prism's navigation concept. The view model locator creates the view model uninitialized, i.e. in an (otherwise) illegal state. It gets valid upon first navigation.
You can live with this and make your view model support the uninitialized state (e.g. drop get-only auto-properties) and load everything when you're navigated to.
Or you can drop Prism's navigation for this view model and create the view model yourself and link it to a data template ("view model first"). Earlier versions of Prism actually supported navigating to a view model (instead of the view), but you can always have a ContentControl bound to a property on the containing view model with a DataTenmplate to show the child view model.
Or you can create your own RegionNavigationService that supports creating a parametrized view model. See this issue at github.

WPF Dependency Object

Has anyone ever heard of implementing IDependencyObject instead of inheriting from it -- that way one could actually create a class hierarchy instead of having to use only interfaces when trying to get both dependency object/property and custom behavior on our classes.
I want to have a hierarchy of class kinds that are directly usable in the context of an existing structure, i.e. Polygon. I want to be able to use my PolyType in any place, and without any more dialogue and indirection that would be required if I place the PolyGon existing type as a Part of my DependencyObject. But I also want to be able to have my class as the a) the target of {Binding} markup extension, b) Animate properties of PolyType and c) apply themed styling to PolyType.
I want to implement IDependencyObject instead of being forced to inherit from it directly, and obstructing my ability to be a direct descendent and usable in place of, PolyGon.
Not sure why you have to inherit from DependencyObject. I use a custom code snippet that generates the following code to register a dependancy property:
public partial class UserControl1 : UserControl
{
public static DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(Polygon), typeof(UserControl1), new FrameworkPropertyMetadata(new PropertyChangedCallback(MyProperty_Changed)));
public Polygon MyProperty
{
get { return (Polygon)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
private static void MyProperty_Changed(DependencyObject o, DependencyPropertyChangedEventArgs args)
{
UserControl1 thisClass = (UserControl1)o;
thisClass.SetMyProperty();
}
private void SetMyProperty()
{
//Put Instance MyProperty Property Changed code here
}
public UserControl1()
{
InitializeComponent();
}
}
As you can see the DependencyObject can be any type of object. If this is not what you need, please post you code examples, or explain your situation better.

WPF Call Method On User Control With Binding

I created a Task List control. I have an AddTask method on it. I'd like to call this method from the host Window.
I found a few posts here in SO and other sites that suggest using an interface, then looping over all the controls in the window to find the control, then getting a reference to it and using that to call the method. Here's an example:
Call method on various user controls
But is it possible to call a method somehow with binding? Assume someone is using MVVM and the Window's VM wants to fire the control's AddTask method. is this possible?
Thanks!
If you really want to do it (in a possible) the right way i'd tell you to write about MVVM.
Binding and methods work very well in MVVM using Commands
Here it is my solution
Create a ViewModel class
Create a nested class MyCommandBehaviour that implements ICommand (some people create the class in a different class)
In the view model create a property MyCommandBehaviour MyCommand
In the constructor of the view model instantiate that property
In The XAML bind the button {Binding MyCommand}
Set the DataContext of the window (or user control) to the view model
Note: I usually create the Command nested class with a constructor that accepts the 'parent' view model. Because the class is nested it can directly access the view model private members
public class OkCommand : System.Windows.Input.ICommand
{
private MyViewModel _vm;
public OkCommand(MyViewModel vm)
{
this._vm = vm;
}
public bool CanExecute(object parameter)
{
return true;//I never use this and the event below
}
#pragma warning disable 0067
public event EventHandler CanExecuteChanged;
#pragma warning restore 0067
public void Execute(object parameter)
{
//do your stuff. Note you can access the MyViewModel members here via _vm
}
}

Using callback interface as a DependencyProperty in WPF?

I apologize for the lengthy question, but I feel like it is necessary to include all of this information.
Until now, I've been using a possibly-unorthodox way of adding UserControls to my applications. Let's say I have a UserControl called Diagnostics that has a button, that when clicked, performs a function that is specific to the application that owns it. For example, if I drop Diagnostics into AppA, I want it to display "A", and if I drop it into AppB, I want AppB to define the behavior so it displays "B".
I typically implement this via a callback interface that is passed to the UserControl's constructor, which is pretty straightforward. Here's some sample "code" that probably won't compile, but is presented just to clarify what I've basically done before, and what I am trying to do:
public interface IDiagnosticsCallback {
void DisplayDiagnostics(); // implemented by owner of Diagnostics UserControl
}
public class MyApp : IDiagnosticsCallback {
public void DisplayDiagnostics() {
MessageBox.Show("Diagnostics displayed specifically for MyApp here");
}
}
public Diagnostics : UserControl {
private IDiagnosticsCallback _callback { get; private set; }
public Diagnostics(IDiagnosticsCallback callback) {
_callback = callback;
}
public void ShowDiagnostics_Click(object sender, EventArgs e) {
_callback.DisplayDiagnostics();
}
}
The problem I had in the past was understanding how to declare a UserControl that takes a parameter in its constructor (i.e. doesn't have a default constructor) in XAML, and apparently you can't. I worked around this with a fairly-inelegant method -- I would give the main panel a name in XAML, and then from code-behind I would create Diagnostics, passing it the necessary callback, and then I would add Diagnostics to the panel's list of children. Gross and violates usage of MVVM, but it works.
This weekend, I decided to try to learn how to do it for a class and a TextBox, and it turns out that all I had to do was to create a DependencyProperty in my UserControl and use databinding. It looks something like this:
public ClassA
{
public void ShowSomethingSpecial()
{
MessageBox.Show("Watch me dance!");
}
}
public MyApp
{
public ClassA Foo { get; set; }
}
public Diagnostics : UserControl
{
public static readonly DependencyProperty SomethingProperty = DependencyProperty.Register("Something", typeof(ClassA), typeof(Diagnostics), new PropertyMetadata());
public ClassA Something
{
get { return (MyApp)GetValue(SomethingProperty); }
set { SetValue(SomethingProperty, value); }
}
// now uses default constructor
public void ShowSomethingSpecial_Click(object sender, EventArgs e)
{
Something.ShowSomethingSpecial();
}
}
MyApp.xaml
<diags:Diagnostics Something="{Binding Foo}" />
So Foo is a property of MyApp, which is databound to the Something DependencyProperty of Diagnostics. When I click the button in the UserControl, the behavior is defined by ClassA. Much better, and works with MVVM!
What I'd like to do now is to go one step further and instead pass a callback interface to my UserControl so that it can get the states of my digital inputs and outputs. I'm looking for something like this:
public Diagnostics : UserControl
{
public interface IDioCallback
{
short ReadInputs();
short ReadOutputs();
void SetOutput( char bit);
}
public IDioCallback DioCallbackInterface {
get { return (IDioCallback)GetValue(DioCallbackInterfaceProperty); }
set { SetValue(DioCallbackInterfaceProperty,value); }
}
// Using a DependencyProperty as the backing store for DioCallbackInterface. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DioCallbackInterfaceProperty = DependencyProperty.Register("DioCallbackInterface",typeof(IDioCallback),typeof(Diagnostics),new PropertyMetadata(0)); // PropertyMetadata is the problem...
}
public class DIO : IDioCallback
{
public short ReadInputs() { return 0; }
public short ReadOutputs() { return 0; }
public void SetOutput( char bit) {}
}
public class MyApp
{
public DIO MyDIO { get; set; }
}
MyApp.xaml
<diags:Diagnostics DioCallbackInterface="{Binding MyDIO}" />
While my code (maybe not the exact code above, but my real project) does compile successfully, it appears that the PropertyMetadata passed to Register is at fault. I get an exception that says "Default value type does not match type of property 'DioCallbackInterface'."
Am I doing something really unorthodox, or is this approach to databinding interfaces actually possible? If not, what are the recommended ways of defining how a UserControl behaves based on the application it's being used in?
The exception you have mentioned because of this:
new PropertyMetadata(0)
You have passed 0 (of type Int32) instead of the null or whatever you like for your interface: IDioCallback.
I cannot say that the way you select is wrong, but you should keep in mind that every user of your UserControl must implement that interface you have defined. If you have several properties that you would like to pass to the UserControl, you can basically discard them via DependencyProperty.
In your case you also would like to inject some logic to the UserControl Button. Let me suppose that this control has only one button. MVVM-way to handle Button.Click event is done via ICommand - you can declare the command property in your ViewModel and use it as data source for data binding in your UserControl as DependencyProperty, passing it properly to the Button.
Also you can have an agreement with all of your data context, and use special name for that property. For example:
public interface IViewModelWithCommand
{
public ICommand TheCommand { get; }
}
Implement it for each data context you need, and use TheCommand property name inside your data template of your UserControl. In the code-behind you can create type validation of DataContext passed to your UserControl, and throw an exception in case the type is not implements your interface
Here several articles you probably should be interested in:
RelayCommand
Commands, RelayCommands and EventToCommand
How to use RelayCommands
Using RelayCommand will simplify your life because you don't need to re-implement interface for every command, instead, you need to pass valid action that you want.

MVVM multiple presentations for same object?

I need to present an object differently, twice.
as a node in a TreeView (navigation/rename)
as 2 TextBoxes (rename/edit content)
public class Item
{
public string Name{get;set;}
public string Content{get;set;}
}
My first solution was to keep things simple:
public class MainViewModel
{
// collection of items (treeview navigation)
public BindingList<ItemViewModel> Items{get;set;}
// selected item (from treeview navigation)
// used for textbox edit
public ItemViewModel SelectedItem{get;set;}
}
public class ItemViewModel
{
// Used for treeview navigation
public bool IsSelected{get;set;}
public bool IsExpanded{get;set;}
public bool IsInEditNameMode{get;set;}
public BindingList<ItemViewModel> Children{get;set;}
public void BuildChildren();
// Used for treeview display/rename
// Used for textbox display/rename
public string Name{get;set;}
// Used for textbox edit
public string Content{get;set;}
}
This works well for a while.
But as the application grows more complex, the view model gets "polluted" more and more.
For example, adding additional presentations for the same view model (Advanced properties, Graph representation, etc)
public class ItemViewModel
{
// Used for Advanced properties
public BindingList<PropertyEntry> Properties {get;set;}
public PropertyEntry SelectedProperty{get;set;}
// Used for graph relationship
public BindingList<ItemViewModel> GraphSiblings{get;set;}
public bool IsGraphInEditNameMode{get;set;}
public bool IsSelectedGraphNode {get;set;}
public void BuildGraphSiblings();
// Used for treeview navigation
public bool IsNavigationInEditNameMode{get;set;}
public bool IsSelectedNavigationNode{get;set;}
public bool IsExpandedNavigationNode{get;set;}
public BindingList<ItemViewModel> NavigationChildren{get;set;}
public void BuildNavigationChildren();
// Used for treeview display/rename
// Used for textbox display/rename
// Used for graph display
// Used for Advanced properties display
public string Name{get;set;}
// Used for textbox edit
public string Content{get;set;}
}
Currently, I'm still using a single view model for multiple presentations, because it keeps the selected item in-sync across all presentation.
Also, I do not have to keep duplicating properties (Name/Content).
And finally, PropertyChanged notification helps updates all presentation of the item (ie, changing Name in navigation updates TextBox/Graph/Advanced properties/etc).
However, it also feels like a violation of several principles (single responsibility, least privilege, etc).
But I'm not quite sure how to refactor it, without writing a lot of code to keep the sync/property notification working/duplicating the model's properties across each new view model/etc)
What I would like to know:
If it were up to you, how would you have solved this?
At the moment, everything is still working. I just feel like the code could be further improved, and that's what I need help with.
How about using inheritance? Have a basic ItemViewModel, then subclass it to create a TreeViewItemViewModel, where you add the properties that relate to the tree-view rendering of this item within the subclass.
Could we,
try separating-out various view-specific-behaviors from the ItemViewModel class.
place/encapsulate the view-specific-behaviors in separate class (Behavior classes).
This gives you flexibility at run-time to instantiate/inject/switch behaviors.
Yes, try to use Strategy pattern for making a cleaner, single responsible, easy to maintain code.

Resources