I'm trying to get into MVVM from a UWP perspective. Everything seems to work almost like on WPF, but I can't get a good ViewModel/View Mapping running.
According to the Microsodt-Specialist: https://channel9.msdn.com/Events/Build/2015/3-635 , starts arround 20 Minutes, there is some new special Syntax for the ResourceDictionaries. I've done all steps, which loads the Dictionary as expected, but the straightforward-mapping doesn't work:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="using:ARGUSnet.RaspberryPiFramework.Designer.Views"
xmlns:vm="using:ARGUSnet.RaspberryPiFramework.Designer.ViewModels"
xmlns:local="using:ARGUSnet.RaspberryPiFramework.Designer.Dictionaries"
x:Class="ARGUSnet.RaspberryPiFramework.Designer.Dictionaries.ViewModelMappingDict">
<DataTemplate x:Key="dtTest" x:DataType="vm:TestViewModel">
<v:TestView />
</DataTemplate>
</ResourceDictionary>
I think the problem herein lies in the fact, I have to define a Key. Although I found some explanations about this, I don't get why this has to be done nowadays.
Anyways, the mapping works, if I do this in the ViewContainer directly:
<Page.Resources>
<DataTemplate x:DataType="vm:TestViewModel">
<v:TestView />
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ContentControl
Content="{Binding CurrentContent}" />
</Grid>
The problem is, all examples are found are working kinda directly, without an explicit Mapping-Dictionary, or work with Freamworks like mvvmlight.
Since this is more for learning purposes, I'd rather make this work without blackboxing the Binding-Mechanics into an Framework.
Is it possible, this is just a Bug in the ResourceDictionary? I also found this Thread, How to associate view with viewmodel or multiple DataTemplates for ViewModel? , but according to my understanding, the question there was about an runtime mapping, while I can bind directly.
Related
I have a grid that I am trying to hook up a visibility converter to. I have a class set up in the HelperObjects namespace, but for some reason the xaml is not picking up the reference. The error message is "The resource BoolToVisConverter could not be resolved" Why won't the xaml pull in this resource? I am sure I am doing something ridiculous here...
Here is the xaml:
xmlns:HelperObjects="clr-namespace:foo.HelperObjects"
...
<Grid Visibility="{Binding isZoneTwoVisible, Converter={StaticResource BoolToVisConverter}}">
The error message is indicating that the resource identified by the key BoolToVisConverter, to which you're trying to bind, cannot be found.
The most likely explanation is that you haven't declared that resource within a scope that can be accessed by your xaml. You'll want to create a StaticResource with a key matching the name you're referencing, within a Resources section of your xaml, the exact location may vary depending on your needs/structure.
Assuming you're doing this within a Window, you could do something like:
<Window>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<Grid Visibility="{Binding isZoneTwoVisible, Converter={StaticResource BoolToVis}}">
<--...Content...-->
</Grid>
</Window>
Note: I haven't included your namespace in front of the BooleanToVisibilityConverter because this is a class which already exists within the framework.
It may be the case you require slightly different behaviour, or don't have access to that class, in which case you may need to add your namespace when defining the resource, e.g. <HelperObjects:BooleanToVisibilityConverter x:Key="BoolToVis"/>
Potentially useful further info about static resources: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/staticresource-markup-extension
Try to specify the converter in UserControlResources or even parent level Grid Resources. Assuming you are using a UserControl.
And your code is unclear as of where you are defining the StaticResource BoolToVisConverter. But generally heres the process.
Look at this sample code below to define your StaticResorce in the UserControl Resources section. Make sure you set the DataContext where isZoneTwoVisible is residing. I am hoping you implemented ImplementINotifyPropertyChanged and said PropertyChanged on your isZoneTwoVisible or isZoneTwoVisible is a DependencyProperty. Note: BooleanToVisibilityConverter is a class that implements an IValueConverter or if your property is a bool you don't even need that class.
<UserControl.Resources>
<BooleanToVisibilityConverter
x:Key="boolToVisibility"></BooleanToVisibilityConverter>
</UserControl.Resources>
You did all the things said above and it still dosen't work, sometimes I specify the relative Source hoping its residing in a UserControl.
Visibility="{Binding isZoneTwoVisible,Converter={StaticResource boolToVisibility},RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}">
I suspect a XAML compiler and/or WPF bug, but I want to make sure I haven't done something wrong here (other than trusting the XAML compiler and/or WPF, that is :) ).
Consider the following XAML for a minimal WPF program that will reproduce the problem:
<Window x:Class="TestxSharedMenuItem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<x:Array x:Key="menuItemValues1" Type="{x:Type s:String}">
<s:String>value #1</s:String>
<s:String>value #2</s:String>
<s:String>value #3</s:String>
</x:Array>
<MenuItem x:Key="menuItem1" x:Shared="False"
ItemsSource="{StaticResource menuItemValues1}"
Header="Shared menu item"/>
</Window.Resources>
<StackPanel>
<Menu HorizontalAlignment="Left" VerticalAlignment="Top">
<StaticResource ResourceKey="menuItem1"/>
<StaticResource ResourceKey="menuItem1"/>
</Menu>
</StackPanel>
</Window>
When the program is run, WPF throws an exception:
XamlParseException: 'Add value to collection of type 'System.Windows.Controls.ItemCollection' threw an exception.' Line number '20' and line position '23'.
The InnerException is:
InvalidOperationException: Element already has a logical parent. It must be detached from the old parent before it is attached to a new one.
This is, of course, exactly what one would expect to see if one were trying to use the same control in multiple places. But using the x:Shared="False" attribute should be causing a new instance of the MenuItem resource object to be returned each time it's retrieved, avoiding that problem.
For some reason, having the x:Array element before the MenuItem element causes the x:Shared attribute to be ignored, resulting in a single MenuItem element being shared when the resource is referenced, and so causing the exception.
Other observations:
Adding x:Shared to the x:Array element doesn't help (it shouldn't, but I figured it was worth checking).
It does not matter whether the MenuItem element is actually referencing the x:Array element or not. The mere presence of the x:Array element is sufficient to cause the problem.
The example above uses MenuItem because this is where I ran across the problem, but the issue happens for other control types too.
Work-arounds include:
Moving the x:Array to a different ResourceDictionary (e.g. in App.xaml). Its presence appears to only affect the dictionary declaration in which it's found. (Doing so may or may not be feasible, depending on where the non-shared control resource needs to be declared).
Moving the x:Array to appear later in the dictionary declaration than the non-shared control resource's declaration. Of course, if the non-shared control resource requires the x:Array element, this doesn't help.
Declaring the x:Array inline with the non-shared control rather than as a separate resource. This solves the dependency issues that might exist in the previous two work-arounds, but of course prevents sharing of the x:Array with other dictionary entries that could use it, and exacerbates the issues surrounding the non-sharable control element (i.e. not only do you have multiple copies of the control element, you get multiple copies of the array it's dependent on as well). E.g:
<MenuItem x:Key="menuItem1" x:Shared="False"
Header="Shared menu item">
<MenuItem.ItemsSource>
<x:Array Type="{x:Type s:String}">
<s:String>value #1</s:String>
<s:String>value #2</s:String>
<s:String>value #3</s:String>
</x:Array>
</MenuItem.ItemsSource>
</MenuItem>
Defining the array in code-behind (e.g. as a C# static readonly field instead of in XAML). E.g.:
public static readonly string[] MenuItemValues = { "value #1", "value #2", "value #3" };
Then for example:
<MenuItem x:Key="menuItem1" x:Shared="False"
ItemsSource="{x:Static App.MenuItemValues}"
Header="Shared menu item"/>
Declaring the collection using different markup. E.g. use ArrayList or a non-generic class that inherits List<T> (since XAML and generics don't play nicely together).
The XAML looks fine to me. Have I done something wrong? Did I violate some "by design" rule the XAML compiler and/or WPF imposes and which I'm not aware of?
This isn't the first time I've run into problems with the x:Array markup extension causing problems (see XAML fails to compile, but without any error message, if user-defined object is first resource and followed immediately by x:Array resource and Spurious XAML errors in Visual Studio when declaring x:Array of x:Reference elements), but I want to check to make sure I'm not overlooking something here and that the XAML I wrote should in fact work as I expect.
Addendum:
For now, lacking an answer explaining that I've written the XAML incorrectly, I'm going to assume that I'm correct in my belief that this is a bug in the XAML compiler and/or WPF. I have submitted a bug report on the Microsoft Connect site, in case anyone else runs into this and would like to chime in:
https://connect.microsoft.com/VisualStudio/feedback/details/2443920/declaring-x-array-element-in-resources-causes-x-shared-attribute-on-later-element-to-be-ignored
Write <ResourceDictionary> explicitely into <Window.Resources>:
<Window x:Class="TestxSharedMenuItem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary> <!-- modified here -->
<x:Array x:Key="menuItemValues1" Type="{x:Type s:String}">
<s:String>value #1</s:String>
<s:String>value #2</s:String>
<s:String>value #3</s:String>
</x:Array>
<MenuItem x:Key="menuItem1" x:Shared="False"
ItemsSource="{StaticResource menuItemValues1}"
Header="Shared menu item"/>
</ResourceDictionary> <!-- modified here -->
</Window.Resources>
<StackPanel>
<Menu HorizontalAlignment="Left" VerticalAlignment="Top">
<StaticResource ResourceKey="menuItem1"/>
<StaticResource ResourceKey="menuItem1"/>
</Menu>
</StackPanel>
</Window>
I had a very similar issue (actually, quite exactly the same, only with UserControl). I was desperate when I tried the above workaround :), but it worked.
I've just tried it out with your example code now, with explicit <ResourceDictionary> it works by me, without it it doesn't.
I've spent the better of half a day trying to make the ItemTemplate of a ListView with a UserControl configurable through means of a DependencyProperty on said UserControl. I've come across some weird inconsistencies regarding the two different Binding methods available on Windows 10 UAP platform ( Binding and x:Bind).
The UserControl looks like this and is part of a custom calendar component.
<UserControl
x:Class="FlowDesigner.UserControls.CalendarDayView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FlowDesigner.UserControls"
xmlns:vw="using:FlowDesigner.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="using:FlowDesigner.UserControls"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
x:Name="DateControl">
<UserControl.Resources>
<DataTemplate x:Key="DefaultDataTemplate" x:DataType="vw:Event" >
<uc:EventListTemplate IsToday="{Binding Date, Converter={StaticResource IsTodayConverter}}"
Date="{Binding Date, Mode=OneWay}"
Summary="{Binding Path=Summary, Mode=OneWay}" />
</DataTemplate>
</UserControl.Resources>
<RelativePanel Background="White" BorderBrush="Black" BorderThickness="1" DataContext="{Binding ElementName=DateControl}">
<TextBlock x:Name="DayText" TextAlignment="Center" VerticalAlignment="Center" />
<TextBlock x:Name="MonthText" TextAlignment="Center" VerticalAlignment="Center" RelativePanel.RightOf="DayText" />
<ListView x:Name="EventList" ItemsSource="{x:Bind Events, Mode=OneWay}"
ItemTemplate="{Binding Path=EventItemTemplate, Mode=OneWay, FallbackValue={StaticResource DefaultDataTemplate}, TargetNullValue={StaticResource DefaultDataTemplate}}"
RelativePanel.Below="DayText" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
</ListView>
</RelativePanel>
</UserControl>
The EventItemTemplate is a DependencyProperty of the UserControl.
public DataTemplate EventItemTemplate
{
get { return (DataTemplate)GetValue(EventItemTemplateProperty); }
set { SetValue(EventItemTemplateProperty, value); }
}
public static readonly DependencyProperty EventItemTemplateProperty =
DependencyProperty.Register("EventItemTemplate", typeof(DataTemplate), typeof(CalendarDayView), new PropertyMetadata(null));
Which is altered on one of the root pages to style the ListView in one way or the other, like so.
<Style TargetType="uc:CalendarDayView">
<Setter Property="EventItemTemplate">
<Setter.Value>
<DataTemplate x:DataType="vw:Event" >
<TextBlock Text="{Binding Summary, Mode=OneWay}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
This is actually a working version, but I had to tinker around with it quite a bit. The first attempts were made by me with both x:Bind and Binding and without the DataContext on the RelativePanel as the UserControl is now. x:Bind would be functional when setting a value to EventItemTemplate in the root page, but it would not be able to use the default DataTemplate specified by the StaticResource when the root page did not specify anything. Binding on the other hand would use the default DataTemplate at all times, even when the the root page had a set an other value to EventItemTemplate.
By setting the DataContext on the RelativePanel to the UserControl Binding started worked like wanted it too. x:Bind still shows the same behavior.
Now I understand that Binding does not by default bind to the UserControl's DataContext, but I'm still not entirely sure why x:Bind doesn't work. Is this intended behavior or is there something wrong with my entire scheme here and is what I came up with just a lucky hack?
From {x:Bind} markup extension:
The {x:Bind} markup extension—new for Windows 10—is an alternative to {Binding}. {x:Bind} lacks some of the features of {Binding}, but it runs in less time and less memory than {Binding} and supports better debugging.
At XAML load time, {x:Bind} is converted into what you can think of as a binding object, and this object gets a value from a property on a data source. The binding object can optionally be configured to observe changes in the value of the data source property and refresh itself based on those changes. It can also optionally be configured to push changes in its own value back to the source property. The binding objects created by {x:Bind} and {Binding} are largely functionally equivalent. But {x:Bind} executes special-purpose code, which it generates at compile-time, and {Binding} uses general-purpose runtime object inspection. Consequently, {x:Bind} bindings (often referred-to as compiled bindings) have great performance, provide compile-time validation of your binding expressions, and support debugging by enabling you to set breakpoints in the code files that are generated as the partial class for your page. These files can be found in your obj folder, with names like (for C#) .g.cs.
This seems like such a basic question but after hours of searching around and not figuring out what I'm doing wrong I decided it's time to ask for help!
I'm new to WPF and the MVVM pattern, but am trying to create an application that has several windows you can navigate through by clicking buttons. This is accomplished by having the app window display UserControls using DataTemplates, so there's no content currently shared between pages (though there will be once I create the navigation area). Here's what the XAML looks like for the main window, with there currently only being one page in the application:
<Window x:Class="WPF_Application.ApplicationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF_Application"
Title="ApplicationView" Height="300" Width="300" WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Window.Resources>
<DataTemplate DataType="{x:Type local:LoginMenuViewModel}">
<local:LoginMenuView />
</DataTemplate>
</Window.Resources>
<DockPanel>
<ContentControl
Content="{Binding CurrentPageViewModel}" />
</DockPanel>
Now what I'd like to do is add a KeyBinding that reacts to the escape button being pressed. When this is done "LogoutCommand" should fire in the LoginMenuViewModel. I'm stuck getting the keybinding to trigger any commands within LoginMenuViewModel, and I've figured it's probably because the DataContext needs to be set to reference LoginMenuViewModel. For the life of me I can't get this to work.
Am I going about application-wide commands completely the wrong way? Is there some super simple fix that will make me smack my forehead in shame? Any insight is appreciated!
I do not know your ViewModel code, so it is not easy to give you details hints.
First of all, if your are using MVVM, you should have your own implementation of ICommand interface. You can find here the most common one (and a simple MVVM tutorial too).
After you have your own command class, your ViewModel should expose your LogoutCommand:
public class ViewModel
{
/* ... */
public ICommand LogoutCommand
{
get
{
return /* your command */
}
}
/* ... */
}
In your code behind you will set: DataContext = new ViewModel(); and at this point you can declare the KeyBindings that you need:
<Window.InputBindings>
<KeyBinding Key="Escape" Command="{Binding Path=LogoutCommand, Mode=OneWay}" />
</Window.InputBindings>
In this way when the Window is active and the user press the "Esc" key, your LogoutCommand is executed.
This is a brief summary that I hope will guide you in deepening the MVVM and its command system.
I have the following in C# and VS2010:
- a MainWindow.xaml with a ListView
- a MainViewModel with an ObservableCollection of objects of type, UnitData
- a UnitDataDisplay.xaml with a bunch of labels tied to members of UnitData
I need to show a variable number of UnitDataDisplays in my ListView that should be based on MainViewModel's ObservableCollection of UnitDatas. What exactly should the syntax be in XAML to bind the ListView Items to multiple UserControl objects without breaking the MVVM separation of concerns? I mean I could easily have my ObservableCollection contain a bunch of UnitDataDisplay objects but that would force my MainViewModel to have knowledge of View details.
My MainWindow XAML file looks like this:
<Window x:Class="ListViewTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="350">
<Grid>
<ListView Height="234" HorizontalAlignment="Left" Margin="12,28,0,0" Name="listView1" VerticalAlignment="Top" Width="304" ItemsSource="{Binding Path=UnitDataDisplay}"/>
</Grid>
</Window>
Any help would be greatly appreciated.
A common approach is to use implicit data templates based on model type (UnitData):
<Window.Resources>
<DataTemplate DataType="{x:Type local:UnitData}">
<local:UnitDataDisplay />
</DataTemplate>
</Window.Resources>
I would then change your ListView to a ListBox, or an ItemsControl if you don't want each item in the list to be selectable.
I would also seriously consider using an MVVM framework rather than the above approach, such as Caliburn.Micro which makes view composition such as this much easier, as well as a host of other advantages.