Extending WPF application - c#

I've got a WPF MVVM application. One of my views has a user control that needs to be customizable for each installation. It's basically a sketch of the customers installation with some labels etc. bound to a viewmodel.
Now my problem is that this user control is different on each site/installation. One approach is to load the xaml from a file/database runtime using a xaml reader. This works but since my viewmodel is generic I have to bind to methods instead of properties and I can't load a xaml with objectdataprovider.
Currently I'm trying to see if MEF can be used so that I can create the user control as a plug-in. So what I'm looking for now is this:
how can I define a user control with view/view model that exports a contract for MEF
How can my parent view (in my wpf app) load the imported user control
Any tips are appreciated, or maybe someone has a different approach?

I suggest you look into Prism in combination with MEF. It has a notion of Modules (plug-ins in your case) and Regions (mechanism of dynamically loading views).
You will be able to export a view using a simple attribute:
[ViewExport(RegionName = RegionNames.MyRegion)]
public partial class MyView : UserControl {
public MyView() {
this.InitializeComponent();
}
[Import]
public MyViewModel ViewModel {
set { DataContext = value; }
}
}
[Export]
public class MyViewModel : ViewModelBase
[
...
}
And in your main application XAML you will be able to import the plugin's views like this:
<ContentControl Regions:RegionManager.RegionName="{x:Static Infrastructure:RegionNames.MyRegion}"/>

One thing I'd consider is the design where you need to install a custom View for each installation. Instead, I'd look to make that View more generic. This will make your design more simple in the long run. Plus, you are setting up for a maintenance nightmare with a different installation for every installed base.
It's a little difficult to tell from your description, but it sounds like the View is a collection of some kind of an object (some kind of drawing with a label or something). Therefore, I'd treat it as such.
I'd create a base abstract class that describes what every object that your View could show. Since I don't have more information, I'll call this thing a "DrawingObject" for lack of a better term. This class would hold all information common to all objects in your View. Note that ObservableItem is a class that implements INotifyPropertyChanged, and SetProperty sets the value in that base class and raises PropertyChanged.
abstract class DrawingObject : ObservableItem
{
Point mPosition;
public Point Position
{
get { return mPosition; }
set { SetProperty("Position", ref mPosition, value); }
}
String mLabelText;
public String LabelText
{
get { return mLabelText; }
set { SetProperty("LabelText", ref mLabelText, value); }
}
}
Then, derive more custom objects from that base class:
class Counter : DrawingObject
{
public Counter() : base()
{
}
}
Your ViewModel would then just have a collection of these objects, using the base class. The set may be private, because you will probably get the objects from someplace in the constructor (i.e. the database, or a flat file, or...)
class ViewModel : ObservableItem
{
public ViewModel() : base()
{
// Call something to populate DrawingObjects property
PopulateDrawingObjects();
}
ObservableCollection<DrawingObject> mDrawingObjects =
new ObservableCollection<DrawingObject>();
public ObservableCollection<DrawingObject> DrawingObjects
{
get { return mDrawingObjects; }
private set { mDrawingObjects = value; }
}
}
Then, your View would bind to this collection and draw them appropriately (I'll leave that as an exercise for the implementer).
One extension that I didn't show is that the DrawingObject may need to implement the appropriate serialization functionality.
Obviously, this is a rough sketch of the design, and may have a couple of errors (I did it from my head), but hopefully it's enough to go on.

Related

WPF inheriting interface in view or in separate class

I'm making a media player program and I have the following interface:
public interface IMediaService
{
void Play();
}
Would it be more appropriate to inherit the interface in the view where the MediaElement control resides and access it directly in the implementation of the methods or rather have it in a separate class like this:
public class MediaPlayer : IMediaService
{
private MediaElement _mediaElement;
public MediaPlayer(MediaElement mediaElement)
{
_mediaElement = mediaElement;
}
public void Play()
{
_mediaElement.Play();
//...
}
}
vs inheriting it in the view:
public partial class MainWindow : IMediaService
{
public MainWindow()
{
InitializeComponent();
}
void IMediaService.Play()
{
Player.Play();
//..
}
}
I'm not using MVVM, but those methods might be used as bindings through commands.
The problem I see in the second approach is that my view class will get cluttered really fast.
I'm open to any alternative solutions that I haven't mentioned, this is just what I've come up with atm.
Would it be more appropriate to inherit the interface in the view where the MediaElement control resides and access it directly in the implementation of the methods or rather have it in a separate class like this:
It doesn't really matter as far as MVVM is concerned. There is no right or wrong really. It's depends on the developer's personal preference. If you don't want to pollute your view, you create a separate class. If you don't mind adding some methods to your view, you don't.
The benefit of using a seperate class is that you may reuse it for several different views/MediaElement.
But the view model only cares about the interface itself, i.e. it has no dependency upon the actual implementation of it.

How Can I bind DataContext to a Generic ViewModel in XAML?

Suppose we have a generic View model like this:
public class MyViewModel<T> : INotifyPropertyChanged where T : Class1
{
private T _objectModel;
public MyViewModel(T object)
{
_objectModel= object;
}
public event PropertyChangedEventHandler PropertyChanged;
}
When I want to bind this View Model to DataContext of my UserControl in XAML, I can not! XAML editor does not find My View Model class. How should I refer to a generic type in XAML?
<UserControl.DataContext>
<s:MyViewModel<T>/> // How should I write this here????
</UserControl.DataContext>
In the above code s is an alias for my workspace, and If I convert my generic View Model to a concrete class it works normally.
When working with XAML, you cannot instantiate a view model with a generic parameter in XAML code.
To work around this, you need to make use of inheritance, here's an example:
public abstract class ViewModel<T>
Usage:
public class MovieViewModel : ViewModel<Movie>
...
public class GenreViewModel : ViewModel<Genre>
Creating a new class for each model seems to be a bit stupid, however this simply isn't true. By making the assumption that each view model contains one model, you've pretty much set yourself up for following this pattern in all view models, as your base view model enforces this constraint.
I personally use the pattern of using a ViewModel<T> base class, where T is the model.
It's certainly a good idea to keep the logic separated from your base view model. A view model for each model is in fact a very good pattern to implement.
There is another way you can achieve what you're after, this is simply removing the generic from the view model base, consider the example:
public class ViewModel
{
public object Model { get; protected set; }
}
Now, if you populate Model with let's say a Movie, then XAML will see it as a Movie, and not an object. Now this is pretty nifty as far as your XAML side goes, however, when you start working with this model in C#, then you're going to have all sorts of problems as you'll have to cast the object to whatever type you are using. So I wouldn't recommend this at all.
Another method of getting around this would be to set the DataContext in code-behind, and if you're going to do that, then, well, only God can save you now. The main ideas around the MVVM design pattern is the separation of View logic and the Business layer (View Models), as soon as your View starts instantiating view models then that nice separation is lost.
Now saying that, there's nothing stopping you from doing this, I've said this many times before. MVVM is a design pattern, not the law, if you want to set the DataContext in code-behind, then fair enough, however it's important that you are aware of the implications.
You could create a class that inherits from your generic ViewModel and use that
public class PersonViewModel : ViewModel<Person>
XAML:
<UserControl.DataContext>
<s:PersonViewModel/>
</UserControl.DataContext>
You're not capable of setting a generic viewmodel in XAML because XAML requires known types at compile time.
Dependency injection is your best bet
public class MyControl : UserControl{
public MyControl(Object viewModel){
this.DataContext = viewModel;
}
}
If your ViewModel is derived from a base class, let's say NonGenericViewModel then you can assign in code behind an object of type NonGenericViewModel to the DataContext. Using this way you still have the benefits of generics and the data binding will also work because the bindings will be made during runtime, no matter what type of object you assign to DataContext as long as it has properties, collections, etc required by your xaml controls.
BaseViewModel<T> : NonGenericViewModel { ... }
NonGenericViewModel : INotifyPropertyChanged { ... }
And in code behind, in the ctor of your xaml.cs:
NonGenericViewModel nonGenVM = new BaseViewModel<person>();
this.DataContext = nonGenVM;
Even this is correct and working:
this.DataContext = new BaseViewModel<Person>();
It depends if you need or not the class NonGenericViewModel in other places.

Including library functions into WPF design paradigm

Given a basic C# library, how do I implement functions of this library into my WPF application to handle appropriately the concepts of Binding and Commands?
I mean, need I write some own wrappers for these library classes in order to implement interfaces such as ICommand or should this be done directly in the library itself?
Some code to get my question more comprehensible:
From the library:
public class Item
{
public string Name { get; set; }
public void DoSomething() { throw new NotImplementedException; }
}
I want to implement the function DoSomething() in my XAML markup without any line of code in that .cs file since that is, from what I've read, the best practice.
(Assuming that an instance of Item is bound to the control)
<Button Command="{Binding DoSomething}"/>
Well, in order to do so, I need to implement the interface ICommand and create a command, but that is, as stated above, unclear to me since I'm using a library here.
Should I write my own Wrapper for the Item class of the API and implement the ICommand interface or is there any other way to archieve this? I've written the library by myself so changes are possible. I'm just not entirely sure about changing the library because if I do so, it is (possibly) bound to WPF.
Hi there if anything your ViewModel should handle any requests on your Model that's it's sole purpose, to get these things to work you need ICommand and if you want some more info here is link with a tutorial on RoutedCommands. If you have your Model and ViewModel defined then you can easily assign tasks to the particular Model through its VM.
P.S. I think you could treat your library as a Model and write a "wrapper" ViewModel to handle operations on it. HTH
UPDATE
Consider following:
class libClass
{
void method()
{
//do something here
}
}
code above would be your model and if you want it to be more readable you could do it this way
class libModel
{
private libClass _libClass;
public libClass LibClass { get; set; }
}
Note
You could implement INotfiyPropertyChanged in your Model to handle any changes if needed of course.
now in your VM how you use the Model
class ViewModel
{
private libModel _libModel;
public libModel LibModel { get; set; }
//after you set up your RoutedCommands
//I declare method within my VM to handle the RoutedCommands don't know
//if it works when you use Property Method
void VMMethod()
{
//use VM's property to invoke desired method from your lib
}
}
and voila! ready "wrapper" for your class with implementation in your VM.
Tip
If you want to know how to do the RoutedCommands here is a link to a tutorial.

Dynamically creating WPF controls from XML and stacking them

I want to run through a series of steps that comprise a complete test. Some of these steps are automatic (so informational) and others require user interaction. Test steps are not known at compile time, they are using MEF to be loaded.
Currently I have something like
public abstract class TestRunnerBase
{
public abstract void Run();
}
With a list of steps like this:
List<TestRunnerBase> Steps = new List<TestRunnerBase>();
So all data representing a test serializable and that works okay so far. However what I really need is for a user to load a test from XML, it then walks them through the options displaying information on screen and gathering results.
But trying to work out how to create a control for data that is unknown at compile time has ended up with me getting a bit stuck on the best approach.
I am thinking to do this I would have a list of custom controls (1 a step) and the GUI would display the first step, wait for that control to be complete (I was thinking here that a raised event might work?) and then display the next if available and so on until the test is complete.
So is it possible to do this in WPF? Can you create a stack of controls in WPF that can each raise the same event to the parent container or is there a better way to do it?
But if I also use the abstract class I can't then derive a control from it also as no multiple inheritance in C# of course.
I would use MVVM and create a viewmodel that understood how to navigate the list of steps, providing a wizard type structure (prev/next) and exposing the current step.
I assume that while you have different kinds of potentially unknown steps that you have a concrete set of input options (bool, text, date, int, etc) then you could use a abstract property on your TestRunnerBase that identifies what kind of input is required (or none) using an enum that must be overriden.
Then you could use datatemplates and/or data triggers to control what is shown for each step of the test. The main viewmodel could check that conditions are right for going to the next step (perhaps a validate on your test).
Some psuedo code to get you thinking:
public enum TestInput
{
None,
Bool,
Text
}
public abstract class TestRunnerBase
{
public abstract TestInput TestInput { get; }
public bool BoolInput { get; set; }
public string TextInput { get; set; }
public abstract bool CanRun()
public abstract void Run();
}
public class MainViewModel
{
List<TestRunnerBase> Steps = new List<TestRunnerBase>();
public TestRunnerBase CurrentStep {get;set;};
public MainViewModel()
{
//loads the Steps
CurrentStep = Steps
}
public Command RunStepCommand
{
if (CurrentStep.CanRun())
{
CurrentStep.Run();
CurrentStep = Steps.Next(); //you get the idea
}
}
}
For your XAML you would bind a ContentPresenter to CurrentStep and use a datatemplate (and maybe data triggers) to control what is visible to the user (and of course bound to the UI).
In WPF you can dynamically create controls using XAML. Simply create a XAML snippet with the layout you want (either programatically or by hand) and use XamlReader.Parse to create the entire tree from the snippet. The returned object can then be inserted somewhere in the visual tree of your window.
To generate events from the visual tree generated by XamlReader you can use routed events.

Dynamic data-entry forms in Silverlight

We are investigating how to create data entry views from a dynamic list of pre-defined field definitions. By "pre-defined", I mean that there are only 8 basic field types. The Silverlight Toolkit's DataForm control is almost what want, but it targets object properties (not a list of custom definitions).
Is there an existing project to make this easy? Please comment on my design idea (below). I have only ~2 weeks Silverlight experience.
Basic design idea:
I am thinking of defining custom data field types. An IEnumerable<BaseDataField> will be received by the UI, enumerated, and controls will be created based on the type of each field. Each field will create a label with the description and BooleanDataField will create a CheckBox, LookupDataField will create a ComboBox, etc.
Pseudo code to clarify the idea:
public abstract class BaseDataField {
public string FieldCode { get; private set; }
public string FieldDescription { get { return FieldDefinitions.Instance.FieldDescription(FieldCode); } }
...
}
public class StringDataField : BaseDataField
public class BooleanDataField : BaseDataField
public class CurrencyDataField : BaseDataField
public class IntegerDataField : BaseDataField
public class NumericDataField : BaseDataField
public class DateTimeDataField : BaseDataField
public class LookupDataField : BaseDataField
public class SpecialDataField : BaseDataField
This will be extended to make the fields bindable; allow specifying custom controls for each type; and have validation feedback.
Can it be easily done in Silverlight or should we create a custom control?
Note: This programme will be a web UI for an existing, multi-tier LOB platform. All data is serialised from a JSON-based REST service.
Thanks!
I would highly recommend incorporating the DataForm class into your solution and it is indeed capable of handling custom controls. I've been chasing this goal of an ideal minimal-xaml data form for years and I have finally settled on what I think is a great combination of the built-in functionality of DataForm and custom fields derived from DataField. For example, I just extended DataField so that when no Content property is specified, it automatically uses a TextBox which minimized my XAML big time. I also added support for the DisplayFormatAttribute which DataForm doesn't seem to support natively.
Anyhow, what you could do first is create a bunch of subclasses like you describe above and derive from DataField. Then in the OnApplyTemplate method, create an element to represent the data. For example:
class BooleanDataField : DataField {
protected override void OnApplyTemplate() {
if (this.Content == null) {
var check = new CheckBox();
check.SetBinding(CheckBox.IsCheckedProperty,
new Binding(this.PropertyPath));
this.Content = check;
}
base.OnApplyTemplate();
}
}
As for how to get your field metadata loaded by DataForm, you have a few options. You could handle the AutoGeneratingField event and look up the appropriate field to use on the fly or you can disable auto field generation and just load it up with your own.
We started with DataForm, but ended up overriding everything that makes it useful and still had some problems. I learned a great deal from the attempt and eventually built a custom control for the job. Unfortunately this solution is not very extensible...

Categories