Proper way to display a view using MVVM - wpf

I am brand new to both wpf and MVVM. I have a Mainwindow that has two views left side has a usercontrol with a listbox and the list box has a edit button inside of it. On the right I have another view that contains all my controls for viewing and editing the record. I can select an item in the list box and edit my record since using binding it automatically populates by the selectedItem object. What I want to do is when the user hits the edit button show the view on the right if they hit another button contained in the list box then show that view on the right and close the previous view. I think I am missing a big concept here since most of the examples are to simplistic and just show one view. I really dont want to have to do it in the code behind. I have looked at John smiths tab and would like to do something similur without the tabs though. Any help would be greatly appreciated.

It sounds like both views need to share the same context (ie ViewModel) then they will stay in synch automaticall by the magic of dependency properties...

I would probably try setting it up so that clicking either button (View or Edit) sets the DataContext of the right frame. The RightFrame View gets displayed using DataTemplates based on the DataContext.
So your xaml would be something like this:
<DataTemplate DataType={x:Type MyEditingViewModel}>
<!-- Editing Object View -->
</DataTemplate>
<DataTemplate DataType={x:Type MyViewingViewModel}>
<!-- Viewing Object View -->
</DataTemplate>
and your button click events would be something like this:
private void EditButton_Click(object sender, EventArgs e)
{
RightFrame.DataContext = new MyEditingViewModel((sender as Button).DataContext)
}
private void ViewButton_Click(object sender, EventArgs e)
{
RightFrame.DataContext = new MyViewingViewModel((sender as Button).DataContext)
}

So basically, what you're trying to do is have your view model decide which view it should be presented in, and make that decision in response to user choice.
How I do this sort of thing:
My view model exposes a view style property, which is an enumerable. In the view for the view model (usually a user control), I implement a DockPanel to contain each style of view. Each DockPanel is assigned a style, and the styles are defined like this:
<Style x:Key="Style_View1" TargetType="DockPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ViewStyle}" Value="View1">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="Style_View2" TargetType="DockPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ViewStyle}" Value="View2">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
(Obviously you don't need to use a DockPanel if a Grid or StackPanel is more appropriate for your scenario. And you can implement a different user control for each style of view if you want to keep your code nicely segmented.)
So as long as the value of ViewStyle is one that there's a corresponding style for, that view style will be visible. Since all of the styles set Visibility to Collapsed by default, there will only ever be at most one view style visible.
There are lots of ways of selecting the view style - create a command for each one and bind it to buttons, create a group of radio buttons and use a value converter, set the view style in response to other properties in the view model - whatever works.

Related

Set property after command

I have a StatusBar below the screen in SL4 (using PRISM), just a very simple Telerik RadDockPanel.
I also have a menu (Telerik RibbonView with RadRibbonGroup and RadRibbonToggleButton). When the toggle button is pressed, I want to set the text to 'ON' and 'OFF', and I want to hide the status bar, but... only in XAML (not using code behind).
I believe this is a common SL/WPF coding practice... but how ?
Have to use EventTrigger (check example bellow on page by link that I provided) and ObjectAnimationUsingKeyFrames to change properties that aren't animated (Text, Visibility so on).
Check good example in other answer on so.
You can specify a DataTrigger in your window like this -
<StatusBar.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=MyRadRibbonToggleButton, Path=IsChecked}"
Value="True">
<Setter Property="Grid.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBar.Style>
In case you can't use ElementName binding then you can use a property in your ViewModel(corresponding to RadRibbonToggleButton state). Similar Trigger can be created for a TextBlock/Label to show On/Off text.
This is how I implement this kind of functionality in WPF/MVVM applications;
You may have to apply some hack to make this work with telerik controls.

WPF: Applying multiple data templates?

Let's say I am displaying a data component, such as TreeView.
Let's say it is bound to a tree structure, of base type TreeViewItem.
TreeViewItem
TreeViewItem
TreeViewItem
TreeViewItem
and so on.
But some of those items are more specific implementations of TreeViewItem such as AnimalTreeViewItem and even more granular ZebraTreeViewItem
TreeViewItem
AnimalTreeViewItem
ZebraTreeViewItem
PlantTreeViewItem
Now, let's say i want these items to be rendered in a similar fashion, but there would be slight differences in rendering depending on underlying type.
One way i've gotten this to work, is using DataTemplate.
Problem is that i have to create a separate template for each type, with 100% of contents defined in the same way (minus small difference in layout / color etc)
Is there a way to define data templates, that share most of their contents together? meaning, w/out having to create 2 templates, that are almost identical in their markup, just to change the background color of some textbox etc..
You might be interested in the solution shown in this article. It works fine if the differences between are minor, e.g. a different color for some element, but it can also handle more complex scenarios through the use of triggers.
The answer to this question is DataTriggers
<DataTemplate x:Key="myTaskTemplate">
...
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=TaskType}">
<DataTrigger.Value>
<local:TaskType>Home</local:TaskType>
</DataTrigger.Value>
<Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
</DataTrigger>
</DataTemplate.Triggers>
...
</DataTemplate>
This data template will only be 'triggered' when the TaskType is Home.
http://msdn.microsoft.com/en-us/library/ms742521.aspx#adding_more_to_datatemplate

The MVVM way of presenting controls dynamically

I have a need to display some kind animation as different processes gets started. My initial idea was to simply add some <ContentControl> tags to the XAML and bind them to a property in the View Model object which then simply assigned this property a ProgressBar, some busy spinner or whatever.
This works but I don't like it. The primary reason I don't like it is because the View Model should not involve itself in presentation matters and this pattern clearly breaks that paradigm.
This is pretty much what my (ugly) code looks like atm:
XAML:
<ContentControl Content="{Binding ProcessAAnimation}" />
In View Model class:
public object ProcessAAnimation
{
get { return _processAAnimation; }
private set
{
_processAAnimation = value;
OnPropertyChanged("ProcessAAnimation");
}
}
public object IsProcessARunning
{
get { return _processARunning; }
private set
{
if (value == _processARunning)
return;
_processRunnings = value;
if (value)
ProcessAAnimation = SomeNiftyAnimationControl();
else
{
if (ProcessAAnimation is IDisposable)
((IDisposable)ProcessAAnimation).Dispose();
ProcessAAnimation = null;
}
}
}
// (clipped: More properties for "Process B", "Process C" and so on)
So, is there a better pattern to achieve this. Preferrably, a pattern where I can create my animation controls dynamically using XAML alone?
Please note that I have already tested a solution where I declare three different animation controls and then bind their Visibility property to the View Model state. That, however, is below par in my book because I don't want to just hide the controls, I want them to be gone unless needed. Besides, that would also make it impossible to dynamically use different types of animations for whatever needs may be.
Anyone?
Well your ViewModel knows about the operation and the progress itself. The rest can be accomplished via Triggers. At least thats the way we do it. So your ViewModel has a property "IsLoadingImage" for example, which is set when your viewmodel starts a BackgroundWorker for loading a big image, it also returns the progress reported by the BackgroundWorker "ImageLoadingProgress" now these two properties are enough to pass to your View. Your view, consists of a Progress bar or a custom control for your special animation. You could now bind the "IsLoadingImage" in a Trigger to toggle the ProgressBar/Animation control visibility and the Value of these is bound to "ImageLoadingProgress".
Like i said, thats how we handle it, and our application makes heavy use of MVVM.
Edit respond to a comment: How to change the template in a trigger
<ControlTemplate x:Name="ActiveTemplate" TargetType="{x:Type MyType}">
<!-- Template when active -->
</ControlTemplate>
<ControlTemplate x:Name="DeactivatedTemplate" TargetType="{x:Type MyType}">
<!-- Template when deactivated -->
</ControlTemplate>
<Style TargetType="{x:Type MyType}">
<Setter Property="Template" Value="{StaticResource DeactivatedTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsActive}" Value="True">
<Setter Property="Template" Value="{StaticResource ActiveTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
This assumes that MyType is a control that can has a ControlTemplate and that the DataContext has a Property IsActive to toggle the Template.

WPF button styling depending on active view

I'm using Caliburn Micro to develop a simple MVVM WPF application.
My ShellView has a single ContentControl and three buttons each which bind to a public method in my ShellViewModel, lets say ActivateView1, ActivateView2 and ActivateView3.
My ShellViewModel inherits from Conductor and each Activate method calls ActivateItem(new View1ViewModel()), etc.
So far so good. When I click a button, a new view gets activated in the ContentControl. The problem is that I need each button to change style when its "associated view" is active and I have really no idea how to achieve this functionality. Do you have any suggestions?
I'm fairly new to Caliburn Micro and WPF-styling so any help will be much appreciated.
I am not very sure about this but still I can think of something like this,
you can create a style and add the style to your button. something like this
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Fill" TargetName="yourButtonName" Value="Black"/>
<Setter Property="Margin" TargetName="yourButtonName" Value="5,0,5,0"/>
</Trigger>
</Style.Triggers>
</Style>
and you can add this style to your button.
I can think of two possible options you could use:
You could bind your buttons style properties to properties on you ShellViewModel. In those properties you can determine the style to return based on the shells Active view i.e.
return ActiveItem == button1ViewModel ?
(Style) App.Current.Resources["Button1ActiveStyleKey"] :
(Style) App.Current.Resources["Button1InactiveStyleKey"];
this would mean your ViewModel would be aware of Styles which you may not want. If that is the case option two would be to write a Caliburn.Micro IResult which changes the style of the button and return 3 of those (one for each button) from a Coroutine that is invoked via the button click i.e.
public IEnumerable<IResult> ButtonOneClicked()
{
yield return new ChangeButtonStyle("Button1Name", "Button1ActiveStyleKey");
yield return new ChangeButtonStyle("Button2Name", "Button2InactiveStyleKey");
yield return new ChangeButtonStyle("Button3Name", "Button3InactiveStyleKey");
}
The implementation of the ChangeButtonStyle IResult would search the view (provided via the ActionExecutionContext parameter to IResult.Execute) for a control with the name provided to the 1st parameter of ChangeButtonStyle ctor, and then set the style property of that control using the resource key provided as the 2nd paramter to the ChangeButtonStyle ctor.
You can use
<Trigger Property ="IsPressed" Value ="True">
I think it does the trick...

How to expose properties of a WPF DataTemplate?

I have a data template that I use in many pages, the data template contains a few buttons, I want to hide some of these buttons by triggers (I mean setting the IsEnabled Property of these buttons in the page where I use this DataTemplate).
In other words, I would even like to set in style triggers/setters a property 'ButtonXIsEnabled', 'ButtonYIsEnabled' as part of the DataTemplate settable from the ListBox where I use this DataTemplate.
I really hope I am clear enough, please leave comments for any further details.
Any discussion will be really appreciated!
Thanks in advance.
Basically this depends on what object your using for your datatemplate. Instead of using some ButtonYIsEnabled, etcs. Try to use some words that fit better in to your domain model.
For example say you have a list of customers, and some of those customers have the ability to purchase discounted products. Then add a property to your Customer called CanPurchaseDiscountedProducts, and use that property in your DataTemplate
<DataTemplate TargetType="{x:Type local:Customer}">
<!-- Other Items -->
<Button Content="Purchase Discounted Products" x:Name="discounts" Visibility="Hidden" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding CanPurchaseDiscountedProducts}" Value="True">
<Setter TargetName="discounts" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
A WPF data template is a view of a certain object type... how you want an instance of ObjectTypeX to look. The data template can bind to properties on the underlying instance.
So if you have a ButtonXIsEnabled property on your instance, you can bind the corresponding Button's Visibility property to the instance property. The button would be shown or hidden based on the value in the underlying object.

Resources