WPF ViewModel overriding values set declaratively in XAML - wpf

I have the following user control:
<HMIClasses:vhMotor x:Name="vhDevice"
AutoSize="{Binding AutoSize,Mode=TwoWay}"
DeviceColour="{Binding DeviceColour,Mode=TwoWay}"
MotorOn="{Binding MotorOn,Mode=TwoWay}"
DeviceSize="{Binding DeviceSize,Mode=TwoWay}"
LayoutDirection="{Binding LayoutDirection,Mode=TwoWay}"
Speed="{Binding Speed,Mode=TwoWay}"
HorizontalAlignment="{Binding HorizontalAlignment,Mode=TwoWay}"
VerticalAlignment="{Binding VerticalAlignment,Mode=TwoWay}"
Margin="{Binding Margin,Mode=TwoWay}" >
This is bound to the datacontext of the (this) user control here:
public ucMotor() : base()
{
Initialise();
InitializeComponent();
this.DataContext = CreateViewModel()
}
When I place the user control in XAML, the DependenyProperties are only applied when I edit them. What I mean is, if I declare the user control and specify properties as such:
<local:ucMotor x:Name="motor1"
DeviceColour="Red"
MotorOn="True"
Speed="Superspeed"
....Sure enough the control responds to the changes within the designer.
If I close and reopen form, the control appears with the properties of the view model and does not reflect those of the XAML. For example, the view model's default colour is silver and the control appears in silver despite the XAM specifying red.If I edit the XAML, the user control is refreshed in the correct colour.
This is my first attempt at user controls and dependency properties, so can someone please tell me where I'm going wrong?
Thanks

Related

WPF: binding elements in main window and user control

I am new to WPF and MVVM and not sure how to solve this (probbaly simple) problem. I have a MainWindow with two regions: RibbonView and below it a TabControl (Telerik). I have big buttons in the ribbon control which are always enabled. For example, I have a button "Tenants" and when I click on this button I create a new tab item and place my TenantControl containing a GridView control (also from Telerik). Its content is automatically loaded.
Now I want to get the following to work: additionally I have some small buttons (Add, Delete, Clone etc) (Ribbon control) that are disabled by default and I want to enable these buttons when I select an item in a grid. Is the using of routed commands the proper way to solve the problem? How to enable a button in a main window if I select an item in a grid in own user control? And yes in a main window I have a reference to the user control. No need for code just a concept is needed - as I said before I am completelly new to WPF/MVVM.
Thanks for any help and best regards,
Erno
In my WPF Applications, I often use RibbonBars as well, but instead of creating different TabItems for the different views, I display them all in one place by using a ContentControl and then just changing the Content value to different view models which are then rendered as the relevant views:
In Mainview:
<ContentControl Content="{Binding ViewModel}" />
...
In MainViewModel:
ViewModel = new SomethingViewModel();
...
In App.xaml:
<DataTemplate DataType="{x:Type ViewModels:SomethingViewModel}">
<Views:SomethingView />
</DataTemplate>
In my MainViewModel class, I have a helper method:
private bool IsViewModelOfType<T>()
{
return ViewModel != null && ViewModel.GetType() == typeof(T);
}
This method is used in the ICommand.CanExecute property to enable and disable Commands dependant on the loaded view model:
public ICommand CopyTrackList
{
get { return new ActionCommand(action => ClipboardManager.SetClipboardText(
((ReleaseLabelCopyViewModel)ViewModel).TrackList), canExecute =>
IsViewModelOfType<ReleaseLabelCopyViewModel>() &&
((ReleaseLabelCopyViewModel)ViewModel).SomePropertyInChildViewModel == true); }
}

How to bind to a method on ViewModel with data from View

I currently have one view with 3 fairly simplistic view models. For the sake of this discussion, we will focus on 2 of the three view models.
The View is a User Management user control. It contains a DataGrid that has its ItemsSource binding set to a UserListViewModel. This view model simply displays user information in the data grid.
The User Management View also contains some other controls, such as buttons for adding new users and removing users. Those buttons are currently bound to a second view model called UserManagementViewModel. For example, the Remove button will successfully call the RemoveUser method on the UserManagementViewModel.
My question is, via XAML (as I hate code-behind), how can I pass the SelectedItem property of the DataGrid (bound to UserListViewModel) into the RemoveUser method call on the UserManagementViewModel? I realize that, in the MVVM design pattern, my view model can't look into the view to retrieve the information necessary, so there must be a way via binding to pass that information into the method.
XAML code examples (or links that show how) to perform similar functionality would be appreciated. Thanks for any help!
you can simply use a commandparameter
<Button Command="{Binding RemoveCommand} CommandParameter="{Binding Elementname=gridUser, Path=SelectedItem}" />
or your UserManagementViewModel have access to the UserListViewModel then you need a command without commandparameter and simply use the SelectedUser property of your UserListViewModel instance
public void ExecuteRemove()
{
var userToRemove = this._myUserListViewModelinstance.SelectedUser;
...
}
I believe what you seek is commanding with a command target bound to the datagrid's selecteditem where one can route such information from the datagrid; say when a button is pressed.
See Commanding Overview on MSDN

How can I easily expose bindable properties of nested controls from a custom UserControl?

My first question here on the Stack. Forgive me for the bad explanation in advance.
I am working on my first MVVM application (Silverlight). I have a custom user control that contains a ListBox to show navigation items. This control is placed in my main xaml page. I don't know if I need to create a composite view model (my main page view model) with a view model especially for the custom control in it or if there is some way to elevate the ListBox properties that I need to bind to.
Through XAML I don't know how to bind, let's say, the ItemsSource property of the ListBox inside the custom control to my main page viewmodel. Basically, I'm at the point that I am questioning my design decision for trying to bind the custom control through my main page view model.
What I have done so far is create dependency properties for the custom control and try to tunnel those dependency properties down to the ListBox properties. I've achieved success with this method for ItemsSource but am having issues with SelectedItem.
Even if I do get SelectedItem to work, it still feels Wrong. Thanks for any advice in advance.
The UserControl should inherit the DataContext from its parent control, unless you are setting it directly. You can then bind to the properties on your view model from your UserControl.
If you would like to create a ViewModel specifically for the UserControl, you can also do that. You would then expose it as a property on your main ViewModel, and bind to it in the MainPage. Example:
public class MainViewModel
{
public ChildViewModel ChildInfo { get; private set; }
}
And then in the view:
<Grid>
...
<lcl:ChildView DataContext="{Binding ChildInfo}" />
...
</Grid>
Your ChildViewModel would then contain properties like SelectedItem to bind your ListBox to.

How do I databind to a ViewModel in Expression Blend?

In WPF and Silverlight you can make a view model object and set it into the DataContext of a control when the control is constructed at runtime. You can then bind properties of the visual object to properties in your DataContext.
One way to set the binding is to type the binding directly into the tag:
<TextBox Text="{Binding Path=Description}"/>
And this will bind the textbox to the Description property in the view model.
The problem with typing in the binding is that you may make a typing mistake. (And you will almost certainly make a mistake if you have hundreds of bindings to make.)
In Expression Blend there is a little white dot beside the Text property in the Properties window. This brings up a menu from which you can Create Data Binding.
How can I get my view model properties to show in the Create Data Binding dialog so that I can select them.
Will the configuration of data binding in Blend interfere with setting my view model into the DataContext at runtime?
One technique is to include the VM as a resource in your view:
<UserControl>
<UserControl.Resources>
<local:YourViewModel x:Key="ViewModel"/>
</UserControl.Resources>
</UserControl>
You can then reference that as DataContext="{StaticResource ViewModel}" elsewhere.
Can't say I like it, but I can't say I like any of the view-first idiosynchrasies that Blend imposes on your design.
I experimented with Blend to find the drag and drop approach to data binding which still lets you override your view model in code easily.
First make your view model object which implements INotifyPropertyChanged and raises the notify event in the setters. The view models can be hierarchical. For example you could have an ObservableCollection within your main view model.
In Blend open up your page or control and go to the data tab.
On the right open the menu under the "Add live data source" icon.
Pick "Define new object data source"
Select your top level view model class and confirm the dialog
In my experiments I found that it was important to bind the data source to where I wanted it first or else Blend might make a less than optimal configuration if I didn't do the next step first.
Open the Objects and Timeline window in Blend
Select the root object, for example UserControl
Open Properties and verify that the root object is selected
Find DataContext and click the square to open the menu and select DataBinding
Select the data source that was just previously created
Now that the data source has been created data binding is very easy.
put some controls on the page
open the Data window
from the DataSource for your view model drag properties onto the controls to create the bindings or set the binding from the Properties window.
Now you can create you live view model object in the constructor of the control
public MainPage()
{
// Required to initialize variables
InitializeComponent();
mVm = new MyViewModel();
this.DataContext = mVm;
}
private MyViewModel mVm;
Add any initialization to retrieve data and you are ready to go.
I have a screen cast on my blog Blendable MVVM : Introduction and Databinding which shows setting this up for Siverlight.
Essentially you create the ViewModel as the DataContext of the UserControl using the "New Object Initialiser" and then using the "Explicit Data Context" tab of the Binding dialog for the controls themselves.
Hope this helps.

Designing WPF UserControl that gets its DataContext from outer controls: How to have some sample data in designer but use inherited DC at runtime?

I am designing a WPF user control which contains other user controls (imagine a WidgetContainer, containing different Widgets) - using M-V-VM architecture.
During development, I have WidgetContainerView in a window, window (View) spawns a WidgetContainerViewModel as its resource, and in a parameterless constructor of WidgetContainerViewModel, I fill its exposed collection with some sample widgets (WidgetViewModels).
WidgetContainer control inherits the DataContext from window, and inside, there is a ListView, that binds Widgets to WidgetView control (which is inside ListView.ItemTemplate).
Now this works OK in my WindowView, as I see my sample widgets, but once I edit the WidgetContainerView or WidgetView, there is no content - at design time, controls are standalone, and they don't inherit any DataContext, so I don't see a content, and have troubles designing them (a ListView is empty, Widget's fields as well...).
I tried adding a sample widget to the WidgetView:
public partial class WidgetView : UserControl
{
public WidgetView()
{
InitializeComponent();
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
//btw, MessageBox.Show(...) here sometimes crashes my Visual Studio (2008), but I have seen the message - this code gets executed at design time, but with some lag - I saw the message on reload of designer, but at that time, I have already commented it - wtf?
this.DataContext = new WidgetViewModel(); //creates sample widget
}
}
}
but that didn't work - I still don't see anything in designer.
I also wanted to create a WidgetViewModel as a resource in WidgetView, like this:
<UserControl x:Class="MVVMTestWidgetsControl.View.WidgetView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="WidgetViewModel" //this doesn't work!
Height="Auto" Width="Auto">
<UserControl.Resources>
<ResourceDictionary>
<ViewModel:WidgetViewModel x:Key="WidgetViewModel" />
</ResourceDictionary>
</UserControl.Resources>
<TextBlock Text="{Binding Path=Title}"></TextBlock>
</UserControl>
but I don't know how to assign a WidgetViewModel as a DataContext of a whole widget - I can't add DataContext attribute to UserControl, because WidgetViewModel is defined later in the code. Any ideas how to do this? I could use a sample data this way, and just override it in code so that it has the right content at runtime...
What are your best practices when developing user controls? Thank you, designing empty control is no fun :)).
In your second snippet, you should be able to refer to your DataContext as a DynamicResource:
DataContext="{DynamicResource WidgetViewModel}"
But most custom user controls have some sort of top level layout container, and you can set the DataContext on that container as a StaticResource.
In your case, however, you may want to consider dropping the VM portion of your code altogether since you're writing a custom UserControl. You should ask yourself what benefits are you gaining from a completely self-contained ViewModel with no real backing Model designed for just one View (i.e. the custom UserControl). Perhaps you could just define some DependencyProperties and use those?
I came up with several solutions: Add DC as resource (it will get automatically instantiated with parameterless constructor), and do the following in View's codebehind:
public PanelView()
{
InitializeComponent();
if (!DesignerProperties.GetIsInDesignMode(new DependencyObject())) //DeleteAtRelease:
{
//we are in runtime, reset DC to have it inherited
this.DataContextHolder.DataContext = DependencyProperty.UnsetValue;
}
}
Better way would be to only assign DC if we are at designtime, but VS didn't like it - it worked only sometimes, and quite nondeterministically, and once it even crashed.
Other check for design time is:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
this.DataContext = new WidgetViewModel();
}

Resources