How to use view models for windows inside a DLL - c#

I'm new to WPF. Here is xaml defining a window defined inside a DLL:
<Window x:Class="MyNamespace.MyClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d1p1:Ignorable="d"
xmlns:attachedProperties="clr-namespace:MyNamespace.AttachedProperties"
xmlns:viewModels="clr-namespace:MyNamespace.ViewModels"
DataContext="{Binding Source={StaticResource VmLocator}}"
Title="{Binding MyVm.MyTitle, Mode=OneTime}" Height="300" Width="460">
<Window.Resources>
<viewModels:ViewModelLocatorTestSteps x:Key="VmLocator" d:IsDataSource="True" />
</Window.Resources>
When the client code constructs this window object, this exception is thrown:
Cannot find resource named 'VmLocator'
How do I define the resource earlier so that it exists when it is needed? I'd also prefer the solution enable Intellisense to work. This is my first attempt at a window defined inside a DLL.
Using Visual Studio 2013.

If you want the Window to create its own DataContext, you can just stick that in the constructor in the code-behind, and avoid the necessity of making your VmLocator a resource. The resources of a WPF control (including a Window) are available to children of that control.
just:
public MyNamespace()
{
InitializeComponents();
this.DataContext = new VmLocator();
}
If you really want to make your DataContext a resource, you could create an application-level resource and reference that.
Also - 'MyNamespace' is a very confusing name for a class :)

Related

How to open WPF application from an other WPF both using MVVM?

I am trying to us the demo code from wpf chrometabs and in the other application I just added a button that calls the constructor of the demo:
private void FrontendDebug_Click(object sender, RoutedEventArgs e)
{
Demo.MainWindow mainWindow = new Demo.MainWindow();
mainWindow.Show();
}
The problem is that it throws an exception at the InitalizeComponent:
I read somewhere that in MVVM, the DataContext should be set before calling InitializeComponent() but I don't know if that is the problem here or how to do it if it is.
The error that you get points out, that you use a StaticResource markup extension to reference a resource with key Locator that is not found in this line:
DataContext="{Binding Source={StaticResource Locator},Path=ViewModelMainWindow}"
The locator is defined in the application resources, so it is accessible in the whole application. Either you accidentially removed it or there is a typo in App.xaml. Make sure that it looks like this.
<Application x:Class="Demo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:Demo.ViewModel" />
</Application.Resources>
</Application>
I am trying to us the demo code from wpf chrometabs and in the other application I just added a button that calls the constructor of the demo:
If you have another application that uses the demo project as library, the call below will only create an instance of the MainWindow. It will not bootstrap the application in the demo project.
Demo.MainWindow mainWindow = new Demo.MainWindow();
Consequently, the App object from the demo project is never created and its resources are not available. Even if it would be bootstrapped, the resources defined in App.xaml only apply to this application, nothing else.
Therefore, the mainWindow instance will try to find the resource in your application, not in the demo application. Since it is not defined there, you will get an exception. You could add the view model locator to your application's App.xaml resources, but keep in mind that is applies to all resources defined on the application level.

The name <...> does not exist in the namespace <...>

I found myself stuck in this little problem that seems to have no solution at all. I'm trying to set the DataContext to a Window in WPF Project that looks like this:
The XAML file:
<Window x:Class="CSB.Tasks.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:CSB.Tasks"
xmlns:vm="clr-namespace:CSB.Tasks.ViewModels.WPF" <!-- This is what i need -->
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<WindowChrome.WindowChrome>
<WindowChrome ResizeBorderThickness="{Binding ResizeBorderThickness}"
GlassFrameThickness="0"
CornerRadius="{Binding CornerRadius}"/>
</WindowChrome.WindowChrome>
<StackPanel Margin="5">
</StackPanel>
I want to set WindowViewModel as the ViewModel of the Window, but VS doesn't seem to find the folders where the class is contained. So, when I try to add the Window.DataContext like:
<Window.DataContext>
<vm:WindowViewModel/>
</Window.DataContext>
VS obviously tells me that the class does not exist.
I've been searching for similar questions on SO and I found plenty of them, but no one actually helped me. I already tried restarting VS, cleaning and rebuilding the project, compiling on a specific target platform (now it's set to Any CPU), moving the ViewModel in the root folder and then moving it back, absolutely no changes.
Does anyone know what could the cause be?
Thank you in advance for the help.
I actually managed to add the DataContext to the MainWindow XAML. The namespace of the ViewModel was set to CSB.Tasks in order to access it globally, but even using the local xmlns I couldn't be able to reference it. I had to change the namespace of the ViewModel according to its actual path in the project folder, so:
namespace CSB.Tasks.ViewModels.WPF
{
public class WindowViewModel : BaseViewModel
{
...
}
}
In order to set the xmlns:vm and to use it in the DataContext declaration. Then I switched the ViewModel namespace back to CSB.Tasks and recompiled the project and for some reason in the XAML editor I could be able to access WindowViewModel from the xmlns:local.
It's not very clear to me if this is a bug or not.
Thank you all for the help!

Why can't a custom ListView have it's own xaml file?

Why can't my custom ListView have it's own xaml file? I have a custom Button and it works with a xaml file with no issues, but not my ListView. The main reason I want to use this approach (rather than be forced to create a Style that is place in the Generic.xaml file) is because I would like to take advantage of the Resources element and place all resources related to the listview within the xaml file:
public sealed partial class MyListView : ListView
{
public MyListView()
{
this.InitializeComponent();
}
}
And here is the associated xaml file:
<ListView
x:Class="App1.MyListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<ListView.Resources>
<!-- I would like to place all related resources here instead of having
them placed in external locations, and then have to open different files to find them. -->
</ListView.Resources>
</ListView>
Although I would expect this should work as well, it seems that this problem is present and it is recommended to use the templated control instead.
I suppose the problem is that assigning the compiler is unable to generate the valid assignment to the Items property of the control, which it is trying to construct from the content of the element. Even when the element is closed immediately it seems to be an issue.
Why not place resources on the Page or inside ListView, rather than deriving your own control?
<Page
x:Class="ListViewResources.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ListViewResources"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<!-- Place all related resources here instead of having them placed in external locations, and then have to open different files to find them. -->
</Page.Resources>
<ListView x:Name="MyListView">
<ListView.Resources>
<!-- Or place related resources here -->
</ListView.Resources>
</ListView>
</Page>

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...

Placing an object defined as a resource in the visual tree

I defined a resource which contains a button like following code.
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="313" Width="481">
<Window.Resources>
<Button x:Key="btnMy">my button</Button>
</Window.Resources>
<!--And now, how can I place 'btnMy' into here?-->
</Window>
And I like to place a control into Window1 by XAML coding.
please help me.
<StaticResource ResourceKey="btnMy"/>
If you use this in more than one place you'll get some nice exceptions...
Edit: It might be of interest to some that these exceptions can be avoided by setting x:Shared to false on the resource in question, that will cause the new creation of a control whereever it is referenced.

Categories