Add 3rd Party Controls to ViewModel in decoupled way - c#

I have XAML like this which lets me add in a 3rd Party map control
<UserControl x:Class="AssemblyName.Views.CustomMapView"
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:local="clr-namespace:AssemblyName.Views"
xmlns:ioc="clr-namespace:AssemblyName.Ioc"
xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
ioc:ViewModelLocator.AutoWireViewModel="True">
<esri:MapView x:Name="customMapView">
<esri:Map x:Name="customMap">
<esri:ArcGISTiledMapServiceLayer ID="BaseMap" ServiceUri="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"/>
</esri:Map>
</esri:MapView>
</UserControl>
All my business logic which happens in the ViewModel, needs to interact with this control and make it do things. Ideally, I would like the the View to have no idea of what type of control it is. I do this all the time with UserControls by making an entry in the XAML like:
<ContentControl Name="menuControl" Content="{Binding MenuControl}"/>
then the ViewModel can set whatever "Menu Control" object that inherits from ContentControl.
since the customMapView doesn't inherit from ContentControl, I can't use the method above that I normally use. It inherits from Control.
Is there a way I can put in a standard <control/> and assign my map control to it?
Basically, I just want to interact with this Map object in the ViewModel in the most decoupled way possible.

As stated on the Content property description of the ContentControl class :
Because the Content property is of type Object, there are no
restrictions on what you can put in a ContentControl.The Content is
displayed by a ContentPresenter, which is in the ControlTemplate of
the ContentControl. Every ContentControl type in WPF has a
ContentPresenter in its default ControlTemplate
So you can still bind your Content property like you used to :
<ContentControl Content="{Binding MyDisplayedControl}"/>

Why not just inherit from ContentControl? It obviously has content.
Then Create a template for your control which contains a ContentControl who is bound by
<ContentControl Content="{Binding RelativeSource="{RelativeSource Mode=TemplatedParent}, Path=Content}"/>
I think thats right anyhow.. i'm on my phone. Man I love Intellisense..
Anyhow, Specifically setting the content of a content control to another control seems redundant.

Related

How do I get data binding to work both ways, when I'm programmatically changing the user control?

I have a WPF application, which uses User Settings, and binds it to a <TextBox> like this:
<Window x:Class="SampleApp.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"
mc:Ignorable="d">
<Grid>
<TextBox x:Name="textbox" Text="{Binding Source={StaticResource Settings}, Path=Default.Folder}"/>
</Grid>
</Window>
The App.xaml looks like this:
<Application x:Class="SampleApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:SampleApp.Properties"
ShutdownMode="OnExplicitShutdown"
Startup="Application_Startup">
<Application.Resources>
<properties:Settings x:Key="Settings"/>
</Application.Resources>
</Application>
And here's the App.xaml.cs:
public partial class App : Application
{
public App()
{
}
private void Application_Startup(object sender, StartupEventArgs e)
{
new MainWindow().ShowDialog();
}
}
This works really great one way: My TextBox always displays the content of MySetting when the window is shown.
The other way around doesn't quite work as I intend. What does work, is when the user manually writes into the TextBox.
What doesn't work, is when I programmatically make changes to the TextBox, like this:
textbox.Text = folderBrowserDialog.SelectedPath;
In this case, MySetting doesn't update until the user types into the TextBox.
My current solution is to do this:
Properties.Settings.Default.MySetting = textbox.Text;
But it defeats the point of having a two-way data binding.
What should I do to have data binding work both ways, even when I'm programmatically changing the user control?
But it defeats the point of having a two-way data binding.
No, not really.
The point of two-way binding is so that when the code modifies the source property the UI's target property is updated, and when the user modifies the target property, the source property is updated.
Two-way binding is definitely not there so that you can assign a value programmatically to the target property and have it reflected in a source property that you could have and should been setting instead.
It should be very rare for someone writing WPF code to ever have to name or interact with a UI element defined in XAML. And when that does happen, it should be only to implement user interface features, such as drag-select, drag & drop, key handling, etc.
Putting it another way: in the MVVM paradigm, the view model data structure is the only thing that non-UI code ought to be dealing with. The binding mechanism provides the mediator between the business logic, represented by the view model (and optionally, a model behind that), and the user interface, represented by the XAML.
Indeed, typically if you were to set the target property explicitly, it would discard the binding, causing it to not work at all.
So, the right way to do this is, in the code-behind, to only ever interact with the MySetting property. If you want to update the value shown to the user, then you need to change the code-behind property that is bound to that value.

Setting background for user control in mainwindow not working

I've created a user control called "TestUserControl"
Inside my Mainwindow.xaml file I called my TestUserControl as follows:
<controls:TestUserControl DataContext="{Binding DataContext}" Background="Blue" />
The problem is the blue background isn't being reflected in the actual user control. Is there something I have to do in TestUserControl.xaml to let it accept the data when called from Mainwindow.xaml?
Thanks in advance.
A User Control as it is definition has no representation of it is properties, it is only visually represented with it is controls inside like panels and more controls. Then you can for instance do the following:
<controls:TestUserControl DataContext="{Binding DataContext}" x:Name="Instance">
<Grid Background="{Binding Background, ElementName=Instance}"/>
<controls:TestUserControl/>
And Apply all the base properties of a Control as you consider depending how you represent the control.

WPF MVVM Why use ContentControl + DataTemplate Views rather than straight XAML Window Views?

Why This?
MainWindow.xaml:
<Window x:Class="MVVMProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ContentControl Content="{Binding}"/>
</Grid>
</Window>
Have your ExampleView.xaml set up as:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vms="clr-namespace:MVVMProject.ViewModels">
<DataTemplate DataType="{x:Type vms:ExampleVM}" >
<Grid>
<ActualContent/>
</Grid>
</DataTemplate>
</ResourceDictionary>
And create the window like this:
public partial class App : Application {
protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
MainWindow app = new MainWindow();
ExampleVM context = new ExampleVM();
app.DataContext = context;
app.Show();
}
}
When it can be done like this?
App.xaml: (Set startup window/View)
<Application x:Class="MVVMProject.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="ExampleView.xaml">
</Application>
ExampleView.xaml: (a Window not a ResourceDictionary)
<Window x:Class="MVVMProject.ExampleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vms="clr-namespace:MVVMProject.ViewModels">
>
<Window.DataContext>
<vms:ExampleVM />
</Window.DataContext>
<Grid>
<ActualContent/>
</Grid>
</Window>
Essentially it's "View as DataTemplate" (VaD) vs. "View as Window" (VaW)
Here is my understanding of the comparison:
VaD: Lets you switch Views without closing the window. (This is not desirable for my project)
VaD: VM knows absolutely nothing about the View, whereas in VaW it (only) has to be able to instantiate it when opening another window
VaW: I can actually see my xaml rendered in the Designer (I can't
with VaD, at least in my current setup)
VaW: Works intuitively with
opening and closing windows; each window has (is) a corresponding View
(and ViewModel)
VaD: ViewModel can pass along initial window width, height, resizability etc. through properties (whereas in VaW they are directly set in the Window)
VaW: Can set FocusManager.FocusedElement (not sure how in VaD)
VaW: Less files, since my window types (e.g. Ribbon, Dialog) are incorporated into their Views
So what's going on here? Can't I just build my windows in XAML, access their data cleanly through properties of the VM, and be done with it? The code-behind is the same (virtually nil).
I'm struggling to understand why I should shuffle all the View stuff into a ResourceDictionary.
People use DataTemplates that way when they want to dynamically switch Views depending on the ViewModel:
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:VM1}">
<!-- View 1 Here -->
</DataTemplate>
<DataTemplate DataType="{x:Type local:VM2}">
<!-- View 2 here -->
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding}"/>
</Window>
So,
if Window.DataContext is an instance of VM1, then View1 will be displayed,
and if
Window.DataContext is an instance of VM2, then View2 will be displayed.
Granted, it makes no sense at all if only 1 View is expected, and never changed.
Since in VaD the view models know nothing about the views, you can build a fully functioning application entirely made up of view models only and no views. This leads to the possibility of writing an application that can be driven entirely by code. This in turn leads to the possibility of performing integration testing without the GUI. Integration testing through the GUI is notoriously fragile - while testing through view models should be more robust.
From my personal experience:
Both work models are aviables, depending of what you want, and depending of the application requirements. The idea behind VaD is decopling the content, and the container. If you implement VaD you can use this template (by default) when ever you show any item of this type. You can use it in ItemsControls (lists, listviews, grids, etc) and in ContentControls only making bindings. Like you said, VaD works for switching the window's content with out closing and opening a new. Also you can define the view using UserControls, then you take control if focused elements, and also you can manage code behind. So, your data template may be like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vms="clr-namespace:MVVMProject.ViewModels">
<DataTemplate DataType="{x:Type vms:ExampleVM}" >
<CustomUserControl A="{Binding A}" B="{Binding B}" DataContext="{Binding}" .../>
</DataTemplate>
You also in an UserControl may set dependency properties, thats make easier the job, because allow bindings and decoupling the app.
But of course, if you app doesn't require dynamically content switching, it is fine to use VaW for the main window, or any other window. In fact, you can use both VaW and VaD. This last one can be used for inner items in the app, that doesn't require windows. You shoose what is better for you, depending of application requirements, and the time aviable for developing the app.
Hope this personal experience helps...

Access controls not properties in XAML User Control

I already know how to compose User Control and access inner control properties with Dependency Properties in XAML User Control.
My question is, how to access a control itself inside of User Control.
For example,
User Control: TbCanvas.xaml
<UserControl
x:Class="Sample.Control.TbCanvas"
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"
d:DesignHeight="300"
d:DesignWidth="400"
x:Name="root">
<Grid>
<Image x:Name="imageMain" />
</Grid>
</UserControl>
I'd like to use it in some Page that has above User Control as below.
SomePage.xaml.cs
this.tbCanvas.imageMain.Source = "some_path";
Of course, the Source can be given as Dependency Property with Binding, but I sometimes need to access inner controls because every property of controls are not entirely bindable.
Check out these links
C# usercontrol how to access all child controls
how to get child user control by id from a parent panel user control?
http://www.daniweb.com/software-development/csharp/threads/141469/how-to-access-child-control-of-a-user-control

How to define the usercontrols in mvvm pattern?

I using from mvvm in my application. I want know how to define my user control in mvvm pattern.
Must I define it by using from mvvm, or I can define it generally?
Let's just call the control that embeds the user control MainWindow, and the user control UserControl. Since you are in MVVM pattern, you have at least one View Model for the outer view - I usually use the name MainVm.
You have two choices for the user control: They can share the same View Model, or you could have a sub view model, just for the UserControl, i.e. UserVm.
For your first choice, you do nothing. You define UserControl (Visual Studio 'add new item' -> User Control is a pretty good start). Then, you simply embed it in Main Window.
<Window
x:Class="SO.MainWindow"
...
xmlns:src="clr-namespace:SO"
...
>
...
<src:UserControl />
...
</Window>
UserControl will inherit the same DataContext from MainWindow, and do all the {Binding} as you would do in the MainWindow.
If you want to have a sub view model (UserVm) - it would typically be a public property of the MainVm (say, userVm). In that case, you'll set the DataContext of the UserControl when you reference it.
<src:UserControl DataContext="{Binding Path=userVm}" />
Another popular paradigm would be to declare the DataTemplate instead of the UserControl. If you do that, you just need to put the UserVm (either instantiate it in the XAML, or through binding):
<Window x:Class="MainWindow" ...>
<Window.Resources>
<DataTemplate x:Key="UserDt"> <!-- or user TargetType instead of x:Key -->
...
</DataTemplate>
</Window.Resources>
...
<!-- You can put in a ContentControl like here: -->
<ContentControl Content="{Binding Path=userVm}"
ContentTemplate="{StaticResource UserDt}" />
<!-- or, if you defined TargetType for the DT, you can simply instantiate
the sub VM here. I don't like this apporach but it exists. -->
<src:UserVm />
</Window>
I think that depends on the user control. The user control can be just a view, in which case you would compose a larger control or page which has this user control as part of the whole. The larger control or page would provide the view and the view model parts for this view.
Or you could create a self contained user control which has all of mvvm and use events to interact with the larger user control that it is a part of.
I suspect you'll get better reuse and modularisation with the second approach.
In short: it depends.

Categories