Let's say I have the following XAML :
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Canvas>
<Button Content="Write something" Canvas.Left="43" Canvas.Top="159" Width="162" Height="42" Click="Button_Click_1"/>
</Canvas>
My ViewModel can be this class, or can contain a instance of it (a viewmodel logic) :
//Here is my static class for extension methods
public static class ExtendenWindowClass
{
/// <summary>
/// Eventhandler for Button
/// </summary>
/// <param name="obj"></param>
/// <param name="sender"></param>
/// <param name="e"></param>
public static void Button_Click_1(this MainWindow obj, object sender, RoutedEventArgs e)
{
MessageBox.Show("Wait 10 seconds");
Thread.Sleep(10000);
MessageBox.Show("Ready, now you can press again");
}
}
So the whiring is no longer in code behind, but in an extension method. The usage of static fields for MainWindow class is minimal, so it can be skipped.
The look of xaml is more natural than with DataBinding objects and curly braces. And I also follow separation of concepts .
What do you think ?
You are just doing it other way around.
There are multiple ways of extending a class. Two of them are -
Partial class.
Extension methods.
In code behind you are extending your class using partial class implementation.
public partial class MainWindow : Window { }
And in code you posted you are achieving that using other way i.e. extension methods. I don't think this way you are getting something more here.
MVVM pattern's main motive is to decouple UI logic from business logic. Also having code in extension method OR in code behind can't be unit tested at all. Code behind and having extension method on window is exactly same for me. Your View and ViewModel should work oblivious to each other so that it can be worked on simultaneously by different developers.
You can read about MVVM more here and here.
Related
Firstly, I am new to WPF and MVVM, am trying hard to write well structured/separated code so please be kind.
I have created a user control and its own separate view model. In the view model I have an ICommand which relays to a method in the same viewmodel. I bind to this command in the XAML using System.Windows.Interactivity on an event like so:-
<UserControl x:Class="MyNamespace.MyControl"
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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:MyNamespace"
mc:Ignorable="d"
Height="300"
d:DesignHeight="300" d:DesignWidth="1500"
IsManipulationEnabled="True"
Background="{StaticResource BackgroundWhiteBrush}">
<Grid
d:DataContext="{x:Static local:MyControlDesignModel.Instance}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseUp">
<i:InvokeCommandAction Command="{Binding MyViewModelCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
My code behind (which I'm trying to leave as empty as possible) looks like this:
namespace MyNamespace
{
/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl : UserControl
{
public MyControl()
{
DataContext = ViewModelMyControl;
InitializeComponent();
}
}
}
I want to be able to use this control in several pages. I also want to be able to call a method in this view model (passing a parameter) from other view models to allow it to update itself from a datastore.
I used a DI container to provide a reference to the view model so that I can a) reference its data loading method from another place and b) set that to the DataContext in the code-behind (above).
The implementation of the DI container provides this as follows:
/// <summary>
/// A shortcut to access the <see cref="MyControlViewModel"/>
/// </summary>
public static MyControlViewModel ViewModelMyControl => Framework.Service<MyControlViewModel>();
With this DI referenced viewmodel on the DataContext, the event/command does not fire.
If I change the code behind to as follows, the event/command does fire but then I lose the static reference which I was trying to have to "hold" the data between pages. I seem to be able to have events or static reference but not both.
namespace MyNamespace
{
/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl : UserControl
{
public MyControl()
{
DataContext = new MyControlViewModel();
InitializeComponent();
}
}
}
I think it has something to do with the ViewModel lifecycle or perhaps binding in general. I have been following a lot of guides and now find myself stuck.
How do I have event/commands firing and also maintain a reference to the user control's data between pages which use it?
I am trying to inherit an usercontrol in WPF the way mentioned in How can a WPF UserControl inherit a WPF UserControl?
namespace DMS.Presentation
{
/// <summary>
/// Interaction logic for WorkSpaceViewControl
/// </summary>
public abstract class WorkSpaceViewControl : UserControl
{
public WorkSpaceViewControl()
{
InitializeComponent();
}
private void InitializeComponent()
{
}
}
}
And code doesn't give any error thus far. But when I inherit it in a new usercontrol:
namespace DMS.Presentation
{
/// <summary>
/// Interaction logic for AnimalWorkSpaceView.xaml
/// </summary>
public partial class AnimalWorkSpaceView : WorkSpaceViewControl
{
public AnimalWorkSpaceView()
{
InitializeComponent();
}
}
}
And it's XAML file is:
//I have tried both WorkSpaceViewControl:UserControl and UserControl:WorkSpaceViewControl here
<UserControl:WorkSpaceViewControl x:Class="DMS.Presentation.WorkSpaceViewControl"
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:DMS.Presentation"
xmlns:WorkSpaceViewControl="clr-namespace:DMS.Presentation"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
</UserControl:WorkSpaceViewControl>
I get a message that partial modifier doesn't exist. Another Partial Declaration of WorkSpaceViewControl exists. So how should I implement it and where have things gone wrong? My whole project is stuck due to this inheritance bottle-neck since January. Help will be really appreciated.
According to the answer you've referenced, your derived UserControl XAML should look more like this:
<local:WorkSpaceViewControl x:Class="DMS.Presentation.AnimalWorkSpaceView"
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:DMS.Presentation"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
</local:WorkSpaceViewControl>
You had declared two different XML namespaces, local and WorkSpaceViewControl, both referring to "clr-namespace:DMS.Presentation". You need just one of them (so I kept local, it being more idiomatic), and you need to use the namespace to qualify the type name WorkSpaceViewControl.
Thus, the XAML declaration starts as <local:WorkSpaceViewControl ...
In addition, the x:Class value for your derived class needs to be the derived class, not the base class. So instead of "DMS.Presentation.WorkSpaceViewControl", that should be set to "DMS.Presentation.AnimalWorkSpaceView" as shown above.
So I have referenced an enum in this example using the staticextension, which works fine at runtime, but fails at design time due to the error "Could not create an instance of type 'StaticExtension'."
I understand this to mean that it thinks it needs an instance of the enum type to actually reference it. However, the enum is defined in the window as static so I don't understand why it is having issues.
Is there any reasonable way to keep the designer working? The closest I have found so far is to put it in an objectdataprovider and create methods to return the enum values. Behind the scenes this is basically creating an object to reference the static type and seems like too much work to just pull the enum values out. The goal here is just to be able to reference individual enum types and display them.
<Window
x:Class="DaedalusGraphViewer.GraphViewerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:DaedalusGraphViewer="clr-namespace:DaedalusGraphViewer">
<StackPanel>
<Label Content="testtesttest">
<Label.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static DaedalusGraphViewer:GraphViewerWindow+Test.test1}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
</StackPanel>
</Window>
C#:
using System.Windows;
namespace DaedalusGraphViewer
{
/// <summary>
/// Interaction logic for GraphViewerWindow.xaml
/// </summary>
public partial class GraphViewerWindow : Window
{
public GraphViewerWindow()
{
InitializeComponent();
Application.Current.MainWindow = this;
}
public enum Test
{
test1, test2
}
}
}
This is a known bug in designer. The workaround is not to use nested types with designer.
I would like to post my NServiceBus subscripiton messages derived from an EventHandler class to a ListView. The ListView is located inside the MainWindow.xaml of the WPF application.
Here is my NServiceBus subscription event handler code. Note: I would like to post the event message to the ListView control in MainWindow.xaml. Any ideas?
namespace EventPublisher.SubscriberDemoWPF
{
public class PublishTrackEventHandler : IHandleMessages<PublishTrackEvent>
{
public void Handle(PublishTrackEvent message)
{
Trace.TraceInformation(message.GetType().Name);
//Need to post event message to ListView control in MainWindow.xaml UI;
}
}
}
Here is my MainWindow.xaml code, which is in the same namespace as my event handler code:
<Window x:Class="EventPublisher.SubscriberDemoWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView Height="260" HorizontalAlignment="Left" Margin="12,12,0,0" Name="lstEvents" VerticalAlignment="Top" Width="479" />
</Grid>
</Window>
Here is the MainWindow.xaml.cs code (typical):
namespace EventPublisher.SubscriberDemoWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//Would normally use listview.items.add("messages");
}
}
From your NSB message handler you could fire an event that has been attached to from the Window. Depending on how you are managing threads, be aware of updating UI elements from threads other than the UI thread. Check out this article in MSDN for events in WPF.
I'm new to WPF data binding.
I have a ListBox on a form that I want to bind to the results of the following method call:
RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)
.OpenSubKey(#"SOFTWARE\Vendor\Product\Systems").GetSubKeyNames();
At the moment I'm doing it at runtime by assigning ListBox.ItemsSource = (method); in the Window_Loaded() event handler. But this means that the source data for the control is non-obvious when looking at the control configuration in the form editor.
Is there a way to configure this binding within the XAML so that it is visible in the form editor, to make the behavior of the code easier to understand?
Most of the examples in the MSDN documentation bind the controls to static resources, like in-line XAML resources. I've noticed that there is an ObjectDataProvider class which provides "[...] the ability to bind to the result of a method." However I am finding the examples in the ObjectDataProvider documentation quite confusing. I'd appreciate some advice on whether that's the right way to do this binding, and if so, what syntax to use when declaring the ObjectDataProvider.
In short, I don't think you can use such a complex statement directly in your XAML. As you've found, it is possible to bind to the result of calling a method of an object via ObjectDataProvider, but your expression is a chain of method calls that I believe cannot be used to source ObjectDataProvider directly in XAML.
You should instead think about implementing a separated presentation pattern such as Model-View-ViewModel to expose the result of your expression via a collection property on a ViewModel that you then bind as the DataContext of your view (Window).
Something like:
MainWindow.xaml
<Window x:Class="WpfApplication10.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ItemsControl ItemsSource="{Binding Items}"/>
</Grid>
</Window>
MainWindow.cs
using System;
using System.Collections.Generic;
using System.Windows;
using Microsoft.Win32;
namespace WpfApplication10 {
public class ViewModel {
public IEnumerable<String> Items {
get { return RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(#"SOFTWARE\Vendor\Product\Systems").GetSubKeyNames(); }
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
DataContext = new ViewModel();
}
}
}