writing a VTK aplication in WPF, trying to follow MVVM - c#

So I'm pretty new to WPF and MVVM, and while I understand the premise, a lot of this stuff is like trying to read hieroglyphs for me.
Basically, my situation is this: I'm using Activiz, a c# wrapper for VTK, which is an image processing/visualization library. So, in this library, there's a WinForms control called vtk:RenderWindowControl, which is an opengl control containing the class that handles all of the visualization functionality. I think it'd be easier to just use WinForms, but that's not really an option for me.
So, to use vtk:RenderWindowControl in a WPF application, I just need to shove it into a WindowsFormsHost and then I can use it more or less just like the example code, in the code behind (if that's the correct term for the .xaml.cs file)
That's fine for a test app, but in practice, I'd like to follow MVVM if possible. This is where I've run into a wall. If "renderControl" lives in the View class, how can I reference it and use it from the ViewModel? I think binding is the answer to that question, but I only really know how to do that for simple types and commands.
Following ideas in another thread I found, I managed to set up something like this answer
My codebehind looks like this:
public partial class RenderPanel_View : UserControl
{
public static readonly new DependencyProperty RWControlProperty =
DependencyProperty.Register("RWControl", typeof(RenderWindowControl), typeof(RenderPanel_View), new PropertyMetadata(null));
public RenderWindowControl RWControl
{
get { return (RenderWindowControl)GetValue(RWControlProperty); }
set { SetValue(RWControlProperty, value); }
}
public RenderPanel_View()
{
// This is necessary to stop the rendercontrolwindow from trying to load in the
// designer, and crashing the Visual Studio.
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) {
this.Height = 300;
this.Width = 300;
return;
}
InitializeComponent();
this.RWControl = new RenderWindowControl();
this.RWControl.Dock = System.Windows.Forms.DockStyle.Fill;
this.WFHost.Child = this.RWControl;
}
}
My .xaml looks like this
<UserControl x:Class="vtkMVVMTest.RenderPanel.RenderPanel_View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vtk="clr-namespace:Kitware.VTK;assembly=Kitware.VTK"
xmlns:rp="clr-namespace:vtkMVVMTest.RenderPanel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
RWControl="{Binding VMControlProperty}">
<Grid>
<WindowsFormsHost x:Name ="WFHost"/>
</Grid>
</UserControl>
So two things. One, That last line of the xaml header is an error, "The member 'RWControl' is not recognized or accessible". I don't really understand why. Second, for what I'm guessing is the ViewModel half of the equation, how is VMControlProperty supposed to be defined?
Am I at least on the right track here, or is this way off base?

Some controls are not MVVM friendly and you have make the ViewModel aware of View interface and allow interact with it directly. Do not open the whole control to the ViewModel it will ruin the ability to write tests, put an interface on top for example IRenderPanelView and open in the interface only the functionality you need to access from ViewModel. You can then create a DP property of this type in the view, set it in the constructor and bind it to ViewModel.View property in xaml.

Related

NullReferenceException in SimpleInjector.Container.Verify when instancing view via XAML

I'm still in the process of getting into WPF and also decided to give Simple Injector a spin. I created a very simple test project based on the WPF integration example provided in the Simple Injector documentation. My current code is pretty much identical to that example or arguably even simpler as I do not yet have any service objects.
What is different is that I do have a simple View with an accompanying ViewModel that is currently the only thing that is on the MainWindow:
<Window x:Class="WpfPlayground.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfPlayground"
xmlns:view="clr-namespace:WpfPlayground.View"
mc:Ignorable="d"
Title="MyApp" Height="450" Width="800">
<Grid>
<view:MyView />
</Grid>
</Window>
My MainWindowViewModel class exists but is currently still empty. Its constructor has no parameters.
This is what I have so far of MyView (the XAML is still just an empty <Grid />):
public partial class MyView : UserControl
{
public MyView(MyViewModel myViewModel)
{
InitializeComponent();
DataContext = myViewModel;
}
}
This is the Bootstrap() method from my Program class:
private static Container Bootstrap()
{
var container = new Container();
container.Register<MainWindow>();
container.Register<MainWindowViewModel>();
container.Register<MyView>();
container.Register<MyViewModel>();
container.Verify();
return container;
}
Now, I get a NullReferenceException inside the MainWindow constructor when InitializeComponent() is called ("Object reference not set to an instance of an object"). The call stack points back to the call to container.Verify() in Program.Bootstrap() (see above). The constructors of neither MyViewModel nor MyView are ever reached.
If I were to take a guess I'd say that the application is not going through the Container to get the instance of MyView. One of the reasons I did this experiment was actually because I was interested to see how Simple Injector would accomplish this as the documentation seemed to imply that this would somehow happen automatically. Could it be there is a step missing from the documentation? Something to register some sort of interceptor? Is this maybe not the right way to use Simple Injector with WPF/XAML after all? Hopefully I won't have to create controls in code?
If I were to take a guess I'd say that the application is not going through the Container to get the instance of MyView
Yes, SimpleInjector is not able to resolve a view that you define inline in the XAML markup like this.
When the InitializeComponent() method is called at runtime, the built-in parser tries to create an instance of the view that you have defined in your XAML markup wihout any knowledge about or reference to any container.
So your code fails when trying to resolve an instance of the MainWindow, because an exception is thrown from the call to InitializeComponent() in its constructor.
The example in the docs that you refer to doesn't define any inline views. This only works for views that have a parameterless constructor.

WPF binding to contentcontrol.child called twice

I am developing a MVVM application
I want to bind to a WindowsFormHost in my application.But WindowsFormHost is not a dependancyproperty.
So in my view model create a new WindowsFormHost and bind it to a child of a contentcontrol.But i encountered that binding called twice when i run the programe.
Any suggestions??
My XAML
<Window x:Class="Demo.View.area"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Demo.View"
mc:Ignorable="d"
Title="area" Height="300" Width="300">
<ContentControl Content="{Binding myWindow}" />
</Window>
my viewModel.cs contain following implementation
public WindowsFormsHost myWindow
{
get
{
return new WindowsFormsHost() { Child = newWindow };
//newWindow defined in another place
}
}
The root issue here is that you create a new object every time the property getter is called. There are very few scenarios in which that would be a good idea, and it certainly isn't in your case.
We can fix the immediate problem by just not doing that. For example:
private readonly Lazy<WindowsFormsHost> _myWindow =
new Lazy<WindowsFormsHost>(() => new WindowsFormsHost() { Child = newWindow });
public WindowsFormsHost myWindow
{
get { return _myWindow.Value; }
}
That will defer creation of the object until the first time the property getter is called, but after that will always return the same value.
But that doesn't really solve your broader problem. There's not enough context in your question to understand why you think this code is useful, but I think it very unlikely to be the code you really want.
Your view model should not be creating view objects at all. You can and should be declaring the WindowsFormsHost and its child in the XAML, just like you would any other view component.
It also doesn't make sense to contain a WindowsFormsHost component as the child of a ContentControl. It's a perfectly valid control all by itself, and does not need a container.
This answer should address the specific issue you're asking about. But I strongly encourage you to rethink your design. It seems all wrong. If you want help with it, post a new question that includes a good [mcve] that has enough detail that shows exactly what broader goal you're actually trying to solve.

Design pattern in WPF

I am making my first WPF application, so this question may seem rather odd. I have been reading about MVVM and so far it has made sense to me. What I don't understand, though, is separating all the XAML.
What I mean is this: I assume you don't place everything in the MainWindow.xaml and just collapse controls based upon what is going to be used. I would think you would want a container that would contain xaml of other files. Is this correct?
How do you go about separating the XAML out so that it isn't just a mash of everything in one file?
How do you go about separating the XAML out so that it isn't just a mash of everything in one file?
There are many ways, including creating a seperate UserControl, CustomControl, Page or Window
For example, if you wanted to pull some XAML out of your MainWindow.xaml, you could create a UserControl (right-click project, Add, New Item..., User Control (WPF)) called MyUserControl.xaml like this:
<UserControl x:Class="WpfApplication1.MyUserControl"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid>
<TextBlock>This is from a different XAML file.</TextBlock>
</Grid>
</UserControl>
and then use this control in your MainWindow.xaml 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:myControls="clr-namespace:WpfApplication1">
<Grid>
<myControls:MyUserControl/>
</Grid>
</Window>
Note that you need to add a reference to the namespace of your UserControl
xmlns:myControls="clr-namespace:WpfApplication1"
I agree with Kevin's answer about UserControl and Window, so I'll just address the follow up question:
So I should switch between various controls that are collapsed/visible in the content of my MainWindow when the user is interacting with my application?
That is a valid option. It might get messy if you are working with a large application.
The other options that I've used are
switching Views in the code behind; i.e. on the click events you can add and remove elements from the page as you would have done in WinForms. This is not pure MVVM and some people will jump down your throat, but I believe MVVM is a tool, not a religion.
provide Views as properties in your ViewModel, and bind to them from your parent View. I don't know if this is pure MVVM, it's nice when you need to dynamically create Views depending on complex conditions but it can get complicated
use DataTemplates, which are essentially rules to determine the View to use based on the type of data that is provided. So if the data is an Address (or AddressViewModel), use the AddressView. If the data is a CustomerViewModel, use the CustomerView. And so on.
DataTemplates are the preferred pattern in my opinion - clean, easy to maintain, and a nice standard. It's trivial to go to the DataTemplate to see how the binding works, whereas the other two options I've given may lead to spaghetti code in the wrong hands.
MSDN has a nice page on DataTemplates in WPF.
When using Caliburn framework you could compose your application using smaller Views and ViewModels and have a shell which binds all those smaller views together. The shell would display one or many views at the same time, depending on how you want your application to behave. The good thing about this - unlike the pattern mentioned above where you hardcode the name of the View/UserControl in other places - is that you just create a ContentControl and bind it to the correct ViewModel property and Caliburn will find the correct View for you by convention.
Let's say you have a ShellViewModel and a ShellView which is just an empty window, and another View/ViewModel where you want to display in your shell at one point. You no longer need to instantiate your views anywhere and just work your way using POCO ViewModels objects:
public class ShellViewModel : Screen
{
public ChildViewModel Child { get; set; }
public void SomeAction()
{
Child = new ChildViewModel(); //Or inject using factory, IoC, etc.
}
}
public class ShellView : Window
{
}
<Window x:Class="WpfApplication1.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org">
<Grid>
<ContentControl cal:View.Model="{Binding Child}" />
</Grid>
</Window>

Understanding WPF deriving WIndow class

I'm sure this is easy, but new to me for WPF using C#. I know about inheriting from classes and have done so many times such as in C# WinForms projects...
public class MyClass : DerivedFromClass
{}
However, stumped in WPF and here's the issue. I want to build my own set of controls to be used as a baseline for a new learning project... preset my own styles, colors, backgrounds, and other functionality. No problem. Start first with a WPF Window and create "MyWindow".
Now, I want to take this baseline "MyWindow" and subclass THAT for yet another class of MySubClassedWindow. So, I create a new Window class, and by default, VS2010 builds the both designer and code portions of the form. I do view code on the MySubClassedWindow and find
partial class MySubclassedWindow : Window
{}
In C# using WinForms, I would just change to (and I've included the class library reference that includes the "MyWindow" declaration.
partial class MySubclassedWindow : MyWindow
{}
When I do, I get a compilation error of
Partial declarations of 'MyNameSpace.MySubclassedWindow' must not specify different base classes
Your base class should just be a class file (not a Window).
So create WindowBase.cs
public class WindowBase : Window
{
// ...
}
In MainWindow (for example) change the xaml.cs file to inherit from WindowBase instead
public partial class MainWindow : WindowBase
{
public MainWindow()
{
InitializeComponent();
}
// ...
}
In MainWindow.xaml, include the namespace for WindowBase and change Window to base:WindowBase like this
<base:WindowBase x:Class="SubclassWindow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:base="clr-namespace:NamespaceForWindowBase"
Title="MainWindow" Height="350" Width="525">
<!--...-->
</base:WindowBase>
Having a base Window class brings a critical drawback, namely that binding to properties in your base class is much more difficult to do (and the currently accepted answer does not solve this problem). What's the point of inheriting if you cannot reference base properties? I have figured out how to set this up after some long hours, and wanted to share in the hopes that others will be spared this pain.
You may need to use things like value converters, which can only be referenced via static binding, which in my case made sense to have in the WindowBase class. I have included an example because I found it difficult to use these converters consistently in both design and run mode.
You cannot set the x:Name property of this inherited Window via XAML, but you may not need to do so if using the below approach. I have included an example of how to set the name, because inheriting from Window will not allow you to set the name at design time in the subclass. I do not recommend relying on the name of the window at design time, but setting d:DataContext should take care of any binding needs for you.
Be warned that in design mode, but not run mode, a copy of WindowBase (or the class specified in d:DataContext) will be instantiated in design mode and used as the binding context. So in very specific cases you may see data discrepancies, but in the vast majority of use cases this approach should suffice.
WindowBase.cs
````
public class WindowBase : Window
{
//User-Defined UI Configuration class containing System.Drawing.Color
//and Brush properties (platform-agnostic styling in your Project.Core.dll assembly)
public UIStyle UIStyle => Core.UIStyle.Current;
//IValueConverter that converts System.Drawing.Color properties
//into WPF-equivalent Colors and Brushes
//You can skip this if you do not need or did not implement your own ValueConverter
public static IValueConverter UniversalValueConverter { get; } = new UniversalValueConverter();
public WindowBase()
{
//Add window name to scope so that runtime properties can be referenced from XAML
//(Name setting must be done here and not in xaml because this is a base class)
//You probably won't need to, but working example is here in case you do.
var ns = new NameScope();
NameScope.SetNameScope(this, ns);
ns["window"] = this;
//Call Initialize Component via Reflection, so you do not need
//to call InitializeComponent() every time in your base class
this.GetType()
.GetMethod("InitializeComponent",
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance)
.Invoke(this, null);
//Set runtime DataContext - Designer mode will not run this code
this.DataContext = this;
}
//Stub method here so that the above code can find it via reflection
void InitializeComponent() { }
}
SubClassWindow.xaml
<local:WindowBase
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:YourProjectNamespace"
x:Class="YourProjectNamespace.SubClassWindow"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type= {x:Type local:WindowBase}, IsDesignTimeCreatable=True}"
Title="SubClassWindow" Height="100" Width="300">
<!--Design-time DataContext is set in d:DataContext. That option does not affect runtime data binding
Replace local:WindowBase with local:SubClassWindow if you need to access properties in SubClassWindow-->
<Grid Background="{Binding UIStyle.BackgroundColor, Converter={x:Static local:WindowBase.UniversalValueConverter}}"></Grid>
</local:WindowBase>
Nothing is needed in the SubClassWindow code behind (not even a constructor).

C# User Control that can contain other Controls (when using it)

I found something about this issue for ASP, but it didn't help me much ...
What I'd like to do is the following: I want to create a user control that has a collection as property and buttons to navigate through this collection. I want to be able to bind this user control to a collection and display different controls on it (containing data from that collection).
Like what you had in MS Access on the lower edge of a form ...
to be more precise:
When I actually use the control in my application (after I created it), I want to be able to add multiple controls to it (textboxes, labels etc) between the <myControly> and </mycontrol>
If I do that now, the controls on my user control disappear.
Here is an example of one way to do what you want:
First, the code - UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty MyContentProperty =
DependencyProperty.Register("MyContent", typeof(object), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
public object MyContent
{
get { return GetValue(MyContentProperty); }
set { SetValue(MyContentProperty, value); }
}
}
And the user control's XAML - UserControl1.xaml
<UserControl x:Class="InCtrl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" Name="MyCtrl">
<StackPanel>
<Button Content="Up"/>
<ContentPresenter Content="{Binding ElementName=MyCtrl, Path=MyContent}"/>
<Button Content="Down"/>
</StackPanel>
</UserControl>
And finally, the xaml to use our wonderful new control:
<Window x:Class="InCtrl.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:InCtrl"
Title="Window1" Height="300" Width="300">
<Grid>
<me:UserControl1>
<me:UserControl1.MyContent>
<Button Content="Middle"/>
</me:UserControl1.MyContent>
</me:UserControl1>
</Grid>
</Window>
I'm having a hard time understanding your question, but I think what you're describing is an ItemsControl using DataTemplates to display the contents of (presumably) an ObservableCollection(T).
A UserControl may not be the best way to do this. You're wanting to add decorations around content, which is basically what Border does: it has a child element, and it adds its own stuff around the edges.
Look into the Decorator class, which Border descends from. If you make your own Border descendant, you should be easily able to do what you want. However, I believe this would require writing code, not XAML.
You might still want to make a UserControl to wrap the buttons at the bottom, just so you can use the visual designer for part of the process. But Decorator would be a good way to glue the pieces together and allow for user-definable content.
Here's a link to a built-in control (HeaderedContentControl) that does the same thing as the accepted answer except that it is an existing control in WPF since .Net 3.0

Categories

Resources