Some UserControl uses converters, which usually look like this:
<UserControl.Resources>
<Converters:CurrentDataConverter x:Key="CurrentDataConverter"/>
</UserControl.Resources>
I would like to have a base user control for interception of converters, for example,
to use Dependency Injection.
Would it be possible?
Thank you.
Converters (or any resource) can be declared at any level of the Xaml hierarchy so the best place to inject them is the global App resource collection (which is searched last for keys).
At any tine during startup you just add instances of the converters as name/value pairs, rather than declaring them in Xaml. That means you have full control over creation and can use an IOC container to create them.
Related
I'm trying to wrap my head around WPF and MVVM.
In my test application I have a MainViewModel with a ChildViewModel property.
Similarly I have a Window that instantiates a MainViewModel and has a child control that should receive MainViewModel.ChildViewModel
For my current application I have a Window with (snipped for brevity)
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Window.Content>
<view:ChildView DataContext="ChildViewModel"/>
</Window.Content>
How do I have my ChildView usercontrol define that it requires a datacontext of the type ChildViewModel and also receive it?
Currently I create it by setting it like so:
<UserControl.DataContext>
<vm:ChildViewModel>
</UserControl.DataContext>
but this creates a new instance of the ChildViewModel.
I tried to make the question as bare boned as possible. Hope it's still clear
Googling turned up a LOT of (contesting) approaches, but I can't see the forest for the trees anymore.
Your UserControl doesn't need to specify it's own ViewModel - you're already creating one and binding it.
That being said, your binding should be specified as:
<view:ChildView DataContext="{Binding ChildViewModel}"/>
The confusion likely stems from the fact that there are two distinctly different approaches to MVVM. Creating the VM within the Xaml's <DataContext> tag is a "view-first" approach - the View is creating and instantiating the ViewModel.
You're currently working in more of a ViewModel-First approach (similar to my series on MVVM), where the ViewModel's create the other ViewModels, and the VIew just binds to them.
Personally, I find VM-first easier to maintain from a code standpoint, and often prefer it. View-first has the advantage of (potentially) providing a better design time experience, which is why many MVVM advocates use that approach.
I have the following (condensed) silverlight xaml for a view / viewmodel:
<UserControl x:Class=MyView>
<UserControl.Resources>
<MyViewModel x:Name="MyViewModel"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource MyViewModel}}">
<UserControl>
Hopefully this looks familiar to you all.
However, I would like to create 2 instances of this same view user control, but to pass in a parameter to the view model to allow me to have slightly different view model data based on a property which I pass into the view model. Something like:
<UserControl x:Class=MyView>
<UserControl.Resources>
<MyViewModel x:Name="MyViewModel" Filter="Some value set at a higher level"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource MyViewModel}}">
<UserControl>
The problem is that I can't hard code the Filter parameter inside the user control, but need to set it at a higher level. Is there a way to obtain the filter parameter from higher up via binding, and what would the syntax look like. I was hoping something like the following:
Either directly from the parent something like:
<MyView>
<MyView.ViewModel Filter="All">
</MyView>
<MyView>
<MyView.ViewModel Filter="Some">
</MyView>
Or from user control looking upwards, something like:
<UserControl.Resources>
<MyViewModel x:Name="MyViewModel" Filter="{Binding FilterTypeFromDataContextHigherUpTheTree}"/>
</UserControl.Resources>
but I don't know if is is possible to directly refer to the static resource view model from the parent in order to set a property, or what the syntax would look like.
I also don't know if there is an easier way to do this, as I suspect my approach is not very elegant.
The real question is how can I pass a parameter into a view model which is a static resourd
Though it's possible to store the ViewModel as a resource, we usually see the ViewModel set as the DataContext of a view. Then, if someone needs to access the ViewModel through the View, they just cast the DataContext to the proper ViewModel type and access it's properties directly.
If you want to go the Binding route for your Filter you have a couple of choices. You can set the Bnding Soure to RelativeSource.TemplatedParent if the child control is placed inside of the parent control in a template (or Style). You could also use ElementName, but only if the named element is inside of the same scope (in your example above, the named element would have to be somewhere inside of MyView).
The final option I can think of would be to expose a DependencyProperty for the filter on MyView and then set the binding of the ViewModel to that property. This would effectively bubble the filter so that it's accessible outside of MyView, but I don't like this approach at all because it's adding properties to the View just for the sake of passing them to the ViewModel. That should never happen. The ViewModel should always be accessible independantly of the View, which is why I recommend exposing it through the DataContext property (inferred) or through a custom property specifically for the ViewModel (explicit).
I sometimes do this with my ViewModels (e.g., instantiate them as a resource). When I do this, I have my VMs extend DependencyObject, for reasons I'll detail later.
I'd recommend that you move them to app.xaml and define them as resources of the application, where they will be available to everyone as a resource of the application. That way you can
<UserControl
DataContext="{StaticResource MyViewModel}" />
In addition, you can bind properties of your ViewModels together. That's why I extend DependencyObject.
Of course, this limits you to a single instance of MyViewModel, which may or may not work in your case. If it doesn't work, I'd define a ViewModel which contains the shared data, then bind to this via a static resource, as in your "looking upward" example.
Current I have some Views linked to ViewModels using code similar to the following:
<Application.Resources>
<DataTemplate DataType="{ x:Type vm:AgeIndicatorViewModel}">
<v:AgeIndicatorView />
</DataTemplate>
</Application.Resources>
I have two questions regarding this:
Does this method allow me to only link one View to each View Model (I think it does improse this limitation on me, but want to be sure)
When using this method, where should I put all of my DataTemplate declarations? At the moment there are only a few, and they are all in App.Xaml - Is there a better location for these, or is App.Xaml fine / Best location?
The most important question is the second really, as at the moment I want to link my ViewModel to my View in this way, as it requires no external libraries etc.
The way my ViewModels are setup, with their Properties and Commands etc is all working already.
Does this method allow me to only link one View to each View Model (I think it does improse this limitation on me, but want to be sure)
Yes. If you're trying to link multiple ViewModels to multiple Views, you need to encapsulate them within a separate VM, and add a new DataTemplate.
When using this method, where should I put all of my DataTemplate declarations? At the moment there are only a few, and they are all in App.Xaml - Is there a better location for these, or is App.Xaml fine / Best location?
App.Xaml is fine, or really any place in the visual hierarchy above where the DataTemplate will be used.
That being said, if the project gets to be a very large scale project, it's often nicer to start using Merged Resource Dictionaries - this allows you to setup resource dictionaries "near" where you define the View/ViewModel pairs, but then use them at a higher level (ie: merge them into App.Xaml).
Specifying the implicit DataTemplate like you do in your question does tie your View-Model to a single View. You can override this at any control level though, so you could have:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:AgeIndicatorViewModel}">
<v:AgeIndicatorView2 />
</DataTemplate>
</Window.Resources>
This would change the view applied to a view-model for the given window. This can be done on any control at any level.
The benefit of doing this at the app-level though is that it is applied across all windows in your application. While my example above would only be applied to a single window.
In general, the app resources is the best place to define these. Since if you have multiple Windows (i.e. Window1 and Window2), then your view-model will always pick up your implicit DataTemplate.
I see a lot statements like
<TextBox x:Name="txtInput" />
or like
<BooleanToVisibilityConverter x:Key="boolToVis" />
Why the x: is needed and what it gives me.
<DockPanel.Resources>
<c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
And here we have also the c:
Thanks for help
It is nothing more than shortcuts to the different namespaces for XML. You can choose them as you like. If you look at the upper lines in your XAML you will find the line:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Change the 'x' to 'wpf' for instance and you will see that you need to change all the 'x:' prefixes in your code to 'wpf:' to make it compile.
The 'c:' prefix references code of your own. Say you have a class library that compiles to MyLib.dll. This library contains a class named MyData. To be able to reference the MyData class you need something like:
xmlns:c="clr-namespace:MyClasses;assembly=MyLib"
in your XAML header.
You can then reference the MyData class in you XAML with c:MyData. But you are entirely free to change the 'c' to 'myfabulousclasses' or anything else you fancy.
The purpose of this? To distinguish classes or members that have the same name, but belong to different dll's.
The x: Prefix
In the previous root element example, the prefix x: was used to map the XAML namespace http://schemas.microsoft.com/winfx/2006/xaml, which is the dedicated XAML namespace that supports XAML language constructs. This x: prefix is used for mapping this XAML namespace in the templates for projects. The XAML namespace for the XAML language contain several programming constructs that you will use very frequently in your XAML. The following is a listing of the most common x: prefix programming constructs you will use:
x:Key: Sets a unique key for each resource in a ResourceDictionary (or similar dictionary concepts in other frameworks). x:Key will probably account for 90% of the x: usages you will see in a typical WPF application's markup.
x:Class: Specifies the CLR namespace and class name for the class that provides code-behind for a XAML page. You must have such a class to support code-behind per the WPF programming model, and therefore you almost always see x: mapped, even if there are no resources.
x:Name: Specifies a run-time object name for the instance that exists in run-time code after an object element is processed. In general, you will frequently use a WPF-defined equivalent property for x:Name. Such properties map specifically to a CLR backing property and are thus more convenient for application programming, where you frequently use run time code to find the named elements from initialized XAML. The most common such property is FrameworkElement.Name. You might still use x:Name when the equivalent WPF framework-level Name property is not supported in a particular type. This occurs in certain animation scenarios.
x:Static: Enables a reference that returns a static value that is not otherwise a XAML-compatible property.
x:Type: Constructs a Type reference based on a type name. This is used to specify attributes that take Type, such as Style.TargetType, although frequently the property has native string-to-Type conversion in such a way that the x:Type markup extension usage is optional.
http://msdn.microsoft.com/en-us/library/ms752059.aspx
http://msdn.microsoft.com/en-us/library/ms753327.aspx
It is part of a namespace. In your example the c: prefix is used to indicate that the MyData tag belongs to this namespace. You may take a look at the following article on MSDN which explains the x: prefix in XAML.
In Silverlight/XAML you have namespaces such as:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
and so elements have namespaced attributes like this:
<TextBlock x:Name="theMessage" Margin="10">Testing...</TextBlock>
When would this be a benefit for me? Would I at some point create another namespace, e.g.:
xmlns:edward="http://www.tanguay.info/web"
so I can put my own name attributes tags, e.g.:
<TextBlock x:Name="theMessage" edward:Name="secondName" Margin="10">Testing...</TextBlock>
And then somehow process both of the name tags, etc.?
XAML is a XML based markup language, thus you can take advantage of namespaces. The primary goal for this approach is to organize your work in smaller units and mantain disambiguity between them. It's the same principle that operates with normal namespaces in .NET (or other programming languages). Tipically in XAML you use
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
namespace to address the base controls (remember that it's just a string, not an address pointing actually to a website).
Other than that, it's common to find reference to other namespaces used to connect to other assemblies (third party or you own) containing business logic or other XAML objects.
xmlns:demo="clr-namespace:MyNamespace;assembly=MyNamespace.Lib"
and in your XAML have something like this
<Grid>
<demo:MyCustomControl />
</Grid>
Where MyCustomControl is control defined in MyNamespace.Lib assembly.
EDIT: just remembered, if you want to keep a XAML-like syntax in your namespace references, you can create alias for them in the form of uri. Check out this example.