Decouple the screens without magic strings - c#

My WPF project will be organised like this :
Screens
Group1
Screen1
View.xaml
ViewModel.cs
Group2
Screen2
View.xaml
ViewModel.cs
To show the Screen1 from the Screen2 I'll use something like this: ScreenManager.Show("Group1.Screen1") This looks (using reflection) in the Screens.Group1.Screen1 namespace for a View and a ViewModel and instantiates them.
How can I eliminate the magic string without coupling Screen1 and Screen2 (I don't want the classes in Screen2 to use the Screen1 namespace). Also I would like some kind of screen discovery (autocompletion/intellisense)
Or maybe some way (automate test) to verify that all calls to ScreenManager.Show are valid.
Update :
I came up with this:
public class ScreenNames
{
public Group1Screens Group1;
public class Group1Screens
{
public ScreenName Screen1;
}
}
public sealed class ScreenName
{
private ScreenName() { }
}
public class ScreenManager : IScreenManager
{
public void Show(Expression<Func<ScreenNames, ScreenName>> x) {}
}
Usage:
screenManager.Show(x=>x.Group1.Screen1);
Not ideal but I suppose violating DRY is still better than magic strings. And I can automatically test (with reflection) that all calls are valid.

You don't need all that ScreenManager stuff in WPF, because the DataTemplate engine can take care of this for you with pure markup.
You can simply databind a particular area of your application with a ContentPresenter and a bunch of DataTemplates. Bind the area to a property of a 'root' ViewModel, and let the 'root' ViewModel implement INotifyPropertyChanged so that WPF knows if you change the ViewModel in that area.
public class RootViewModel : INotifyPropertyChanged
{
public object Screen1ViewModel { get; }
public object Screen2ViewModel { get; }
}
Databind one ContentPresenter control to the Screen1ViewModel property using
<ContentControl Content="{Binding Path=Screen1ViewModel}" />
and similarly for the next one. When you need to change the content of Screen1, you simply re-assign Screen1ViewModel from code, and because of the raised PropertyChanged event, WPF will pick it up and bind the new ViewModel to a new View.
The DataTemplates may be as simple as this:
<Window.Resources>
<DataTemplate DataType="{x:Type foo:MyViewModel}">
<self:MyControl />
</DataTemplate>
<DataTemplate DataType="{x:Type foo:MyOtherViewModel}">
<self:MyOtherControl />
</DataTemplate>
</Window.Resources>
In case you are not familiar with it, this article on MVVM in WPF is an excellent introduction.

Finally I used T4 code generation to generate my ScreenNames class. I did that by adapting this code : Auto generate strong typed navigation class for all user controls in ASP.NET web application

Related

Binding TabControl to seperate views based on property

Background:
I am trying to bind a tab control to a lot of different views using an interface for the ItemSource.
I was able to successfully do this using just
<Controls:MetroAnimatedSingleRowTabControl x:Name="ModuleTabControl"
ItemsSource="{Binding TabItems}">
<Controls:MetroAnimatedSingleRowTabControl.Resources>
<DataTemplate DataType="{x:Type ViewModels:MainPageViewModel}">
<Views:MainPageView/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:ALDViewModel}">
<Views:ALDView/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:PumpingViewModel}">
<Views:PumpingView/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:VerticalFlowPondViewModel}">
<Views:VerticalFlowPondView/>
</DataTemplate>
</Controls:MetroAnimatedSingleRowTabControl.Resources>
<Controls:MetroAnimatedSingleRowTabControl.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Header}"/>
</DataTemplate>
</Controls:MetroAnimatedSingleRowTabControl.ItemTemplate>
</Controls:MetroAnimatedSingleRowTabControl>
where Controls:MetroAnimatedSingleRowTabControl is just like a normal tab control.
This method worked for me when I was only binding to a property of ObservableCollection<ViewModelBase> TabItems however I now feel as if instead of holding a collection of ViewModels that it is better for me to have an interface as such which I will inherit from
public interface ITabItem
{
ViewModelBase ViewModel { get; set; }
}
and those inherited classes will be stored in the collection of
ObservableCollection<ITabItem> TabItems
I managed to get the Header to display by changing the {Binding Header} with
{Binding ViewModel.Header} and that works.
Question:
How would I link up the DataType property to the TabItems ViewModel Property where I use {x:Type ViewModels:ALDViewModel} ?
Thank you in advance for your help
It is not clear how exactly are you going to use your interface and how it will be related to your ViewModel classes. Either there is not enough info, or you probably not working with interfaces properly. Why would you store view model instance inside an interface? That makes no sense to me, since your view model should implement this interface, not be stored inside.
So, basically, if your ViewModels are going to implement mentioned
ITabItem, your code should work as it is, since actual
types in your TabItems collection will stay the same. Let me
demonstrate.
Say, you previously had something like this:
public ObservableCollection<ViewModelBase> TabItems { get; set; }
// add view model to collection
TabItems.Add(new ALDViewModel());
After using interfaces, it will end up to this:
// view model now implements interface
public class ALDViewModel : ViewModelBase, ITabItem
public ObservableCollection<ITabItem> TabItems { get; set; }
TabItems.Add(new ALDViewModel());
And since DataTempltes defined in XAML basically telling your TabControl how to display types, and your types (view models) stay the same, no changes needed.
If, however, some other type will implement interface and store view model inside and you want to bind to that view model... Well, as I mentioned, that makes no sense and it is not how you should be dealing with view models and interfaces.
I'm not sure about your scenario, but if you want to move some common logic concerning tabcontrol to separate place, you could create, say, TabViewModel class where all general logic for tabs is stored. It may look like this:
/// <summary>
/// Common class for all tab viewmodels
/// </summary>
public abstract class TabViewModel : ViewModelBase
{
/// <summary>
/// Tab name showed to the user
/// </summary>
private string tabName;
/// <summary>
/// Gets or sets the tab name
/// </summary>
public string TabName
{
get
{
return tabName;
}
set
{
tabName = value;
OnPropertyChanged();
}
}
And then use it like this:
public class ALDViewModel: TabViewModel
public ObservableCollection<TabViewModel> TabItems { get; set; }
TabViewModel aldTab = new ALDViewModel();
aldTab.TabName = "ALD";
TabItems.Add(aldTab);
Then again, your XAML code should stay the same and it is probably most effective way to deal with tabs in MVVM.

inherit or encapsulate View/Viewmodel

I want to encapsulate a view/viewmodel into another view/viewmodel. The aim is to have a popup, slide panel or whatever you can think of common behaviour and insert a custom view/viewmodel into it.
I want to use the generic for doing that but I'm kind of stuck
the parent view model will look like that
public class SidePanelViewModel<T>
{
public SidePanelViewModel(T enclosedViewModel)
{
EnclosedViewModel = enclosedViewModel;
}
public T EnclosedViewModel { get; private set; }
}
The parent view will be like that
... whatever design and behavior ...
<ContentPresenter Content="{Binding EnclosedViewModel}"/>
... whatever design and behavior ...
In my mapping file I would like to put that but here is the problem:
<DataTemplate DataType="{x:Type WPFTests:SidePanelViewModel}">
<WPFTests:SidePanelView />
</DataTemplate>
<DataTemplate DataType="{x:Type WPFTests:EnclosedViewModel}">
<WPFTests:EnclosedView />
</DataTemplate>
This does not work because the constructor of the viewmodel SidePanelViewModel requiere a type to be created.
I guess what I want to do is having a view/viewmodel inherit from another view/viewmodel
Hope someone can help on that
Why do you need to use <T>?
Can't you just use a regular object or some kind of shared base such as IViewModel or ViewModelBase?
Working with generic classes in WPF can be quite painful sometimes ..
I would recommend you to create a base (abstract) class that implements the default behavior for your (virtual, of course) methods.

GUI idea for WPF, though no clue how to implement it

What I've got is basicly two classes Plugin and PluginLauncher
Plugin is an abstract class that implements some functions to make a class a Plugin for my PluginLauncher class.
PluginLauncher is a class that holds a collection (SortedDictionary) including some helper functions to start, stop, restart all or a specific Plugin.
It also loads all plugins on initialisation. Each plugin can be a .exe or .dll with a class the inherits from Plugin.
An AppDomain is created for each plugin and communication is also setup for each Plugin (through a simple IPC messaging through sockets) (still has to be implemented)
I want to have a very, VERY basic GUI implementation that just has a list of all loaded Plugins, noting the plugin name, its state (which can be running, stopping, stopped, stoppedprematurely (an Enum)) and a button per plugin to start, stop or restart it.
I know I can add this functionality programmaticaly by just placing the elements on the GUI and calculating each X/Y location etc. but I am sure WPF has some pre-made 'functionalities' for this. But I am quite new to WPF and have no clue where to start looking.
A simple note: I am limited to .net 3.5 (or lower) thus no 4.0 elements.
I've included a very simple (hooray mspaint skills) example of what I had in mind.
The plugin nature of your app has little bearing on the mechanics of how you achieve this. Essentially, you need a collection of view models. Each item in that collection represents a plugin (but it could equally represent a customer or a chicken drumstick). You then bind an ItemsControl to this collection and define a template for how the item should be rendered.
Here is some pseudo-code to get you on your way:
public class PluginViewModel : ViewModel
{
public string Name { get; }
public PluginState State { get; private set; }
public ICommand StartCommand { get; }
public ICommand StopCommand { get; }
public ICommand RestartCommand { get; }
}
public class PluginLauncherViewModel : ViewModel
{
// use an ObservableCollection<PluginViewModel> to store your plugin view models
public ICollection<PluginViewModel> Plugins { get; }
}
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Plugins}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<UniformGrid Rows="1">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Status}"/>
<Button Command="{Binding StartCommand}">Start</Button>
<Button Command="{Binding StopCommand}">Stop</Button>
<Button Command="{Binding RestartCommand}">Restart</Button>
</UniformGrid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
Some problems you will no doubt bump into:
the DataContext of the outer XAML (ie. the ScrollViewer in my above example) must be an instance of PluginLauncherViewModel. How you wire this up is up to you, and there are varied options. Start with something simple like setting it in your code-behind.
ViewModel is a base class for all view models. See here for an example.
Your implementation of ICommand should be MVVM friendly. See here for an example.
For the simplest approach you might consider the Table element. For a more fine-grained control I'd recommend you using a Grid.

Including partial views when applying the Mode-View-ViewModel design pattern

Consider that I have an application that just handles Messages and Users I want my Window to have a common Menu and an area where the current View is displayed.
I can only work with either Messages or Users so I cannot work simultaniously with both Views. Therefore I have the following Controls
MessageView.xaml
UserView.xaml
Just to make it a bit easier, both the Message Model and the User Model looks like this:
Name
Description
Now, I have the following three ViewModels:
MainWindowViewModel
UsersViewModel
MessagesViewModel
The UsersViewModel and the MessagesViewModel both just fetch an ObserverableCollection<T> of its regarding Model which is bound in the corresponding View like this:
<DataGrid ItemSource="{Binding ModelCollection}" />
The MainWindowViewModel hooks up two different Commands that have implemented ICommand that looks something like the following:
public class ShowMessagesCommand : ICommand
{
private ViewModelBase ViewModel { get; set; }
public ShowMessagesCommand (ViewModelBase viewModel)
{
ViewModel = viewModel;
}
public void Execute(object parameter)
{
var viewModel = new ProductsViewModel();
ViewModel.PartialViewModel = new MessageView { DataContext = viewModel };
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
And there is another one a like it that will show Users. Now this introduced ViewModelBase which only holds the following:
public UIElement PartialViewModel
{
get { return (UIElement)GetValue(PartialViewModelProperty); }
set { SetValue(PartialViewModelProperty, value); }
}
public static readonly DependencyProperty PartialViewModelProperty =
DependencyProperty.Register("PartialViewModel", typeof(UIElement), typeof(ViewModelBase), new UIPropertyMetadata(null));
This dependency property is used in the MainWindow.xaml to display the User Control dynamicly like this:
<UserControl Content="{Binding PartialViewModel}" />
There are also two buttons on this Window that fires the Commands:
ShowMessagesCommand
ShowUsersCommand
And when these are fired, the UserControl changes because PartialViewModel is a dependency property.
I want to know if this is bad practice? Should I not inject the User Control like this? Is there another "better" alternative that corresponds better with the design pattern? Or is this a nice way of including partial views?
why not use a ContentPresenter/ContentControl with a datatemplate in your mainwindow?
instead of UserControl Content="{Binding PartialViewModel}" />, you can use a:
<ContentPresenter Content="{Binding Path=PartialViewModel}" />
all you have to do: is set your PartialViewmodel to your child viewmodel and create a datatemplate, so wpf will know how to render your childviewmodel
<DataTemplate DataType={x:Type UserViewModel}>
<UserView/>
</DataTemplate>
<DataTemplate DataType={x:Type MessageViewModel}>
<MessageView/>
</DataTemplate>
when ever you set your PartialViewmodel in your MainViewmodel, the right View will render in your ContenControl.
Edit 1
at least you have to implement INotifyPropertyChanged in your ViewModel and fire it when ever the PartViewModel property is set.
Edit 2
if you use Commands in your viewmodels take a look at some mvvm framework implementations like DelegateCommand or RelayCommand. handling ICommand become much easier with this. within your mainviewmodel you can create commands simple like that
private DelegateCommand _showMessageCommand;
public ICommand ShowMessageCommand
{
get
{
return this._showMessageCommand ?? (this._showMessageCommand = new DelegateCommand(this.ShowMessageExecute, this.CanShowMessageExecute));
}
}
This isn't a bad approach at first sight, it might be just fine to use in a small app.
However, there are a couple of things that aren't that nice:
ViewModelBase needs to be a DependencyObject to have a DependencyProperty. In the real world I 've found that it's very annoying to have to treat ViewModels in a single-threaded manner (there are lots of async operations one might want to perform).
It doesn't scale; changing the layout will require significant amounts of work.
Any decent MVVM framework makes UI composition easy by providing infrastructure to compose sub-Views into your main View. In Prism (which is my personal preference), this happens with Regions.
I would look at using an MVVM framework such as Caliburn.Micro which makes view composition incredibly easy. If you have a property on your view model which is a view model type, and a ContentControl on your view which is named the same as your property, then Caliburn.Micro will locate that view models corresponding view via conventions, do the binding for you automatically, and inject the view into the ContentControl.
I would also avoid using dependency properties on your view models, and instead implement INotifyPropertyChanged. Caliburn.Micro comes with a PropertyChangedBase type which implements this interface, and also provides a helper method for invoking the PropertyChanged event using lambda expressions rather than magic strings (which is much better for refactoring later).
EDIT
http://msdn.microsoft.com/en-us/library/ms743695.aspx shows an example of implementing INotifyPropertyChanged.
To achieve what you want to do in Caliburn.Micro, you would do something like the following (a crude example, but it shows you how easy it is doing view composition using an MVVM framework):
public class MainViewModel : Conductor<IScreen>.Collection.OneActive
{
private UsersViewModel usersViewModel;
private MessagesViewModel messagesViewModel;
public UsersViewModel UsersViewModel
{
get { return this.usersViewModel; }
set { this.usersViewModel = value; this.NotifyOfPropertyChanged(() => this.UsersViewModel);
}
public MessagesViewModel MessagesViewModel
{
get { return this.messagesViewModel; }
set { this.messagesViewModel = value; this.NotifyOfPropertyChanged(() => this.MessagesViewModel);
}
public MainViewModel()
{
this.UsersViewModel = new UsersViewModel();
this.MessagesViewModel = new MessagesViewModel();
this.Items.Add(this.UsersViewModel);
this.Items.Add(this.MessagesViewModel);
// set default view
this.ActivateItem(this.UsersViewModel);
}
public ShowUsers()
{
this.ActivateItem(this.UsersViewModel);
}
public ShowMessages()
{
this.ActivateItem(this.MessagesViewModel);
}
}
Note that UsersViewModel and MessagesViewModel would derive from Screen.
To invoke the ShowUsers or ShowMessages verbs with Caliburn.Micro, you just need to create view controls with the same name. The conductor type has an ActiveItem property which is the currently conducted item, so you can add a ContentControl to your MainView.xaml which is named ActiveItem, and Caliburn.Micro will take care of injecting the correct view.
So your MainView.xaml may look like:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinition>
<!-- Menu in left hand column -->
<StackPanel Grid.Column="0">
<Button x:Name="ShowUsers">Show Users</Button>
<Button x:Name="ShowMessages">Show Messages</Button>
</StackPanel>
<!-- Currently active item -->
<ContentControl x:Name="ActiveItem" Grid.Column="1" />
</Grid>
you should take a look at prism. It gives you region handling.
I would also take a look at MEF to Export Views and on this way maintain an extensibility for your project.

XAML inline data binding doesn't work; code behind binding works

Greetings folks!
I'm running into a problem with WPF databinding that I hope you can help out with. I'm new to WPF but an expereienced developer (VB 3.0-6.0, C#).
Here's the scenario:
I have a C# project called MasterPartsData which contains a number of classes which reprsent different types of parts (capacitor, diode, etc). They inherit from a base class called clsPart.
I have another C# WPF project which contains WPF UserControls (as well as a MainWindow) to visually represent the values stored in an individual MasterPartsData (MPD) object. I've created a private field in the usercontrol to hold the object with a getter and setter.
If I create a binding explicitly in the setter for the populated object:
_capacitor = value;
Binding binding = new Binding();
binding.Source = _capacitor;
binding.Path = new PropertyPath("C0uf");
this.txtC0uf.SetBinding(TextBox.TextProperty, binding);
(with _capacitor being the private object variable and C0uf being the property name)
the value correctly displays.
However I don't wish to have to explicitly create each binding in the code behind. My preference is to create the bindings inline in XAML, perhaps with a DataContext pointing to the object.
Unfortunately every different permutation I've tried fails to work; the text box doesn't show data.
I have a couple of suspicions:
1) The binding is correct, but the text box needs to be refreshed.
2) The binding is confused between the private variable and the properties.
3) Maybe the fact that the class is defined in a different project is causing issues.
4) I'm going mad and should check myself into an asylum before someone gets hurt. :)
Any help you can provide would be most appreciated. I'm more than happy to add more information, but didn't want to clutter the question with pages and pages of source.
With respect to your suspicions:
1) I think the default binding behavior of a TextBox is TwoWay, with a LostFocus update trigger, meaning that your UI focus will have to change to another control before the binding will update, if changes are made in the UI.
If changes are made in the code you need to raise the NotifyPropertyChanged event in order for the binding system to see it.
2) This is probably not the case, but it leaves the impression that you're trying to set bindings on your UserControl properties, which is not the way data binding was designed to be used in this particular kind of use case. What you want is to bind data from non-UI classes to dependency properties on your UserControls.
3) This will never matter, as long as your UI project has a reference to your classes.
4) This is a common reaction people have when beginning to use XAML and WPF. It's like instead of being handed a box of Legos, you just got handed an injection molding machine with insufficient instructions, isn't it?
Overall, this is a situation where you might need to examine your design; elements of the "Model-View-ViewModel" pattern will come in handy. If you're unfamiliar with this, it's a development pattern in which you introduce a "ViewModel" class, perhaps you can call it MasterPartsVM which contains an implementation of INotifyPropertyChanged.
The DataContext of your UserControl would be set to this MasterPartsVM class.
A brief code example, using some generic names. Given a ViewModel class with a small backing class that looks like this:
class PartViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public PartClass Data { get; set; }
public String SomeVMProperty
{
get { return Data.SomeProperty; }
set
{
if (Data.SomeProperty != value)
Data.SomeProperty = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("SomeVMProperty"));
}
}
}
class PartClass
{
public string SomeProperty { get; set; }
}
The XAML of a basic UserControl would look like this:
<UserControl x:Class="WpfApplication1.PartUserControl"
... >
<Grid>
<TextBox Text="{Binding SomeVMProperty}" Margin="68,77,104,176" />
</Grid>
</UserControl>
To connect your data class to this UserControl, you set the UserControl's DataContext property. If you do this in code, it's a matter of having a reference to your user control and the ViewModel, and then setting the property:
MyUserControlInstance.DataContext = new PartViewModel(); // or some existing PartViewModel
That combination of code should work to produce a textbox whose Text property changes every time the SomeVMProperty property is changed.
In a basic binding scenario, if your class looks like this
public class MasterPartsData
{
private string _c0uf;
public string C0uf
{
get { return _c0uf;}
set { _c0uf = value;}
}
public MasterPartsData()
{
C0uf = "Hello World!";
}
}
your XAML would look like this
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" >
<Window.DataContext>
<local:MasterPartsData />
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding Path=C0uf}" />
</Grid>
</Window>
Note, there are many different approaches to setting the DataContext, you don't necessarily just have to do it in the XAML
Also, typically your MasterDataParts class would implement INotifyPropertyChanged

Categories