I'm trying to use the FolderBrowserDialog from my WPF application - nothing fancy. I don't much care that it has the Windows Forms look to it.
However, when I call ShowDialog, I want to pass the owner window which is an IWin32Window. How do I get this from my WPF control?
Actually, does it matter? If I run this code and use the ShowDialog overload with no parameters it works fine. Under what circumstances do I need to pass the owner window?
Thanks,
Craig
And here's my final version.
public static class MyWpfExtensions
{
public static System.Windows.Forms.IWin32Window GetIWin32Window(this System.Windows.Media.Visual visual)
{
var source = System.Windows.PresentationSource.FromVisual(visual) as System.Windows.Interop.HwndSource;
System.Windows.Forms.IWin32Window win = new OldWindow(source.Handle);
return win;
}
private class OldWindow : System.Windows.Forms.IWin32Window
{
private readonly System.IntPtr _handle;
public OldWindow(System.IntPtr handle)
{
_handle = handle;
}
#region IWin32Window Members
System.IntPtr System.Windows.Forms.IWin32Window.Handle
{
get { return _handle; }
}
#endregion
}
}
And to actually use it:
var dlg = new FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dlg.ShowDialog(this.GetIWin32Window());
If you specify Owner, you will get a Modal dialog over the specified WPF window.
To get WinForms compatible Win32 window create a class implements IWin32Window like this
public class OldWindow : System.Windows.Forms.IWin32Window
{
IntPtr _handle;
public OldWindow(IntPtr handle)
{
_handle = handle;
}
#region IWin32Window Members
IntPtr System.Windows.Forms.IWin32Window.Handle
{
get { return _handle; }
}
#endregion
}
And use an instance of this class at your WinForms
IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle; // 'this' means WPF Window
folderBrowserDialog.ShowDialog(new OldWindow(mainWindowPtr));
I realize this is an old question, but here is an approach which might be slightly more elegant (and may or may not have been available before)...
using System;
using System.Windows;
using System.Windows.Forms;
// ...
/// <summary>
/// Utilities for easier integration with WinForms.
/// </summary>
public static class WinFormsCompatibility {
/// <summary>
/// Gets a handle of the given <paramref name="window"/> and wraps it into <see cref="IWin32Window"/>,
/// so it can be consumed by WinForms code, such as <see cref="FolderBrowserDialog"/>.
/// </summary>
/// <param name="window">
/// The WPF window whose handle to get.
/// </param>
/// <returns>
/// The handle of <paramref name="window"/> is returned as <see cref="IWin32Window.Handle"/>.
/// </returns>
public static IWin32Window GetIWin32Window(this Window window) {
return new Win32Window(new System.Windows.Interop.WindowInteropHelper(window).Handle);
}
/// <summary>
/// Implementation detail of <see cref="GetIWin32Window"/>.
/// </summary>
class Win32Window : IWin32Window { // NOTE: This is System.Windows.Forms.IWin32Window, not System.Windows.Interop.IWin32Window!
public Win32Window(IntPtr handle) {
Handle = handle; // C# 6 "read-only" automatic property.
}
public IntPtr Handle { get; }
}
}
Then, from your WPF window, you can simply...
public partial class MainWindow : Window {
void Button_Click(object sender, RoutedEventArgs e) {
using (var dialog = new FolderBrowserDialog()) {
if (dialog.ShowDialog(this.GetIWin32Window()) == System.Windows.Forms.DialogResult.OK) {
// Use dialog.SelectedPath.
}
}
}
}
Actually, does it matter?
I'm not sure if it matters in this case, but generally, you should tell Windows what is your window hierarchy, so if a parent window is clicked while child window is modal, Windows can provide a visual (and possibly audible) clue to the user.
Also, it ensures the "right" window is on top when there are multiple modal windows (not that I'm advocating such UI design). I've seen UIs designed by a certain multi-billion dollar corporation (which shell remain unnamed), that hanged simply because one modal dialog got "stuck" underneath another, and user had no clue it was even there, let alone how to close it.
OK, figured it out now - thanks to Jobi whose answer was close, but not quite.
From a WPF application, here's my code that works:
First a helper class:
private class OldWindow : System.Windows.Forms.IWin32Window
{
IntPtr _handle;
public OldWindow(IntPtr handle)
{
_handle = handle;
}
#region IWin32Window Members
IntPtr System.Windows.Forms.IWin32Window.Handle
{
get { return _handle; }
}
#endregion
}
Then, to use this:
System.Windows.Forms.FolderBrowserDialog dlg = new FolderBrowserDialog();
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
System.Windows.Forms.IWin32Window win = new OldWindow(source.Handle);
System.Windows.Forms.DialogResult result = dlg.ShowDialog(win);
I'm sure I can wrap this up better, but basically it works. Yay! :-)
//add a reference to System.Windows.Forms.dll
public partial class MainWindow : Window, System.Windows.Forms.IWin32Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
var fbd = new FolderBrowserDialog();
fbd.ShowDialog(this);
}
IntPtr System.Windows.Forms.IWin32Window.Handle
{
get
{
return ((HwndSource)PresentationSource.FromVisual(this)).Handle;
}
}
}
VB.net translation
Module MyWpfExtensions
Public Function GetIWin32Window(this As Object, visual As System.Windows.Media.Visual) As System.Windows.Forms.IWin32Window
Dim source As System.Windows.Interop.HwndSource = System.Windows.PresentationSource.FromVisual(Visual)
Dim win As System.Windows.Forms.IWin32Window = New OldWindow(source.Handle)
Return win
End Function
Private Class OldWindow
Implements System.Windows.Forms.IWin32Window
Public Sub New(handle As System.IntPtr)
_handle = handle
End Sub
Dim _handle As System.IntPtr
Public ReadOnly Property Handle As IntPtr Implements Forms.IWin32Window.Handle
Get
End Get
End Property
End Class
End Module
Here is a simple method.
System.Windows.Forms.NativeWindow winForm;
public MainWindow()
{
winForm = new System.Windows.Forms.NativeWindow();
winForm.AssignHandle(new WindowInteropHelper(this).Handle);
...
}
public showDialog()
{
dlgFolderBrowser.ShowDialog(winForm);
}
The advantage of passing an owner handle is that the FolderBrowserDialog will not be modal to that window. This prevents the user from interacting with your main application window while the dialog is active.
You should be able to get an IWin32Window by by using PresentationSource.FromVisual and casting the result to HwndSource which implements IWin32Window.
Also in the comments here:
Why not using the built in WindowInteropHelper class (see namespace System.Windows.Interop). This class already impelements the IWin32Window ;)
So you can forget about the "OldWindow class" ... the usage stays the same
Related
I'm using Catel in my application. I have any questions regarding DataObjects and ViewModels - what is the best way to use Catel efficientlu?
Scenario 1:
I have a MainViewModel and a MainView. In this View I call another View (DataWindow) with a own ViewModel (SettingsViewModel) and show it in a Dialog. In this Dialog I insert some SettingsValues and store it in xml. Last but not least I have a DataObject class to store the data from the Dialog. Here any pseudocode:
MainViewModel : ViewModelBase
{
private void OnSystemSettingsCommandExecute()
{
//create a new ViewModel and show as Dialog
uiVisualizerService.ShowDialog(new SystemSettingsViewModel());
}
...
}
SystemSettingsViewModel : ViewModelBase
{
/// <summary>
/// Gets or sets the property value.
/// </summary>
[Model]
public SettingsDataObject SettingsData
{
get { return GetValue<SettingsDataObject>(SettingsDataProperty); }
set { SetValue(SettingsDataProperty, value); }
}
/// <summary>
///
/// </summary>
public static readonly PropertyData SettingsDataProperty = RegisterProperty("SettingsData", typeof(SettingsDataObject));
/// <summary>
/// It is right to define the property again here?
/// </summary>
[ViewModelToModel("SettingsData")]
public string UserName
{
get { return GetValue<string>(UserNameProperty); }
set { SetValue(UserNameProperty, value); }
}
/// <summary>
/// Register the UserName property so it is known in the class.
/// </summary>
public static readonly PropertyData UserNameProperty = RegisterProperty("UserName", typeof(string));
// Load and Save right here?
protected override bool Save()
{
SettingsData.Save(#"D:\Projects\Testdata\xml\Settings.xml");
return true;
}
protected override void Initialize()
{
SettingsData = SavableModelBase<SettingsDataObject>.Load(#"D:\Projects\Testdata\xml\Settings.xml", SerializationMode.Xml);
}
}
public class SettingsDataObject : SavableModelBase<SettingsDataObject>
{
// Propertys
/// <summary>
/// Gets or sets the property value.
/// </summary>
public string UserName
{
get { return GetValue<string>(UserNameProperty); }
set { SetValue(UserNameProperty, value); }
}
/// <summary>
/// Register the UserName property so it is known in the class.
/// </summary>
public static readonly PropertyData UserNameProperty = RegisterProperty("UserName", typeof(string), "MyUserName");
}
Is it right that I must define the property "UserName" in the DataClass and in the ViewModel class? Is that the "normal way" that I define my Model property in the ViewModel and than i access my data propertys with [ViewModelToModel("SettingsData")]?
How I can Load and Save automatic my Data Objects? In my case I override the "Save" and the "Initialize" methode? Is there a better way to do this in Catel?
Now I must have access of the SettingsDataObject in the MainViewModel but i didn't find a way to use the object in other ViewModels. What is the "best practices" to load the settings in other ViewModels?
Question 1)
Yes, this is "right", but totally depends on the form of MVVM you want to follow. For more information, read this. I like to protect my model (make it private on the VM) and only expose the properties I really want being exposed on the VM. But some other people like to directly bind on the model instead. It's just the different number of interpretations like you can read in the article.
Question 2)
Overriding the Initialize and Save are exactly meant for this, so you are doing it the right way!
Question 3)
When you need to share a model, you can either use nested user controls to pass the model from one view model to another. If you need to access the model in a lot of different places, it might be wise to register it in the ServiceLocator. It can then automatically be injected into your view models by Catel.
// Somewhere in your code
var serviceLocator = ServiceLocator.Default;
serviceLocator.RegisterType<ISettings>(mySettings);
// Your view model constructor
public MyViewModel(ISettings mySettings)
{
// injected here
}
If you want to create your own view model, you can do this:
var dependencyResolver = this.GetDependencyResolver();
var viewModelFactory = dependencyResolver.Resolve<IViewModelFactory>();
var viewModel = viewModelFactory.CreateViewModel<MyViewModel>(null);
Note that if you are inside another VM, you can of course let the IViewModelFactory be injected so you only need 1 line of code to create the view model.
I'm tasked with creating something of a 3D interface for an application. After looking through my options I decided Viewport2DVisual3D was the easiest to understand and use with my game design background (I am comfortable with matrix transformations, etc).
So far I've got a nice list of buttons displaying in a semicircular 'stage' sort of pattern onscreen. This is a good start. Now, what I want to do is allow the carousel to rotate with user input. For now, I'd have a button rotate into center view when clicked, but eventually I'll use manipulation data to allow the screen to be swiped about.
The issue I'm having is binding the Transform property of each V2DV3D to the data backing each Button control. I am not sure how to do this in code-behind, and it needs to be code-behind in order to programmatically build the transforms.
As it is, I assign the value this way (vv is a Viewport2DVisual3D object):
vv.SetValue(Viewport2DVisual3D.TransformProperty, item.Transform);
Where item is a CarouselItem which implements INotifyPropertyChanged:
public class CarouselItem : INotifyPropertyChanged
{
[...]
public Transform3DGroup Transform
{
get { return _transform; }
set { _transform = value; OnPropertyChanged("Transform"); }
}
[...]
private void Recalc()
{
Transform3D rotate = new RotateTransform3D(new
AxisAngleRotation3D(CarouselBrowser.Up, angle));
Transform3D translate = new TranslateTransform3D(0, 0,
CarouselBrowser.Radius);
Transform3D translate2 = new TranslateTransform3D(
CarouselBrowser.Focus);
Transform3DGroup tGroup = new Transform3DGroup();
tGroup.Children.Add(translate);
tGroup.Children.Add(rotate);
tGroup.Children.Add(translate2);
Transform = tGroup;
}
[...]
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
[...]
}
(some code omitted; Recalc is called when angle is changed, and the new value is different than the old)
So the initial 'binding' works fine, the buttons take their initial transformations, but any further modifications to Transform result in no change. The event handler for the Transform property change event has no subscribers. How should I change the binding/relationship with CarouselItem and V2DV3D to get them to link? There doesn't seem to be any sort of DataContext or similar property of V2DV3D I could bind the whole object to.
Well this question and this question are similar but no answers that work. In fact I was hoping WindowStartupLocation=CenterOwner would work...it doesn't. It seems to center the new window in the center of a grid column, not the center of the main window. So I'm assuming it thinks that is the parent. Second when I close the dialog and open it again it is not centered but moved down and right from the previous position. And if I move the main window to a second monitor the popup still opens on the default monitor. Are these properties wrong or am I just thinking it should work in a different way. I suppose I could calculate the Top and Left properties manually. I just want the popup to be centered in the main window no matter where it is.
Probably because you didn't set the owner:
this.Owner = App.MainWindow; // for example
That's how I do it and it centers the window perfectly all the time.
To extend on what Will Eddins commented, you could create an overload method for ShowDialog() or Show() in your Window:
public void ShowDialog(Window owner)
{
this.Owner = owner;
this.ShowDialog();
}
public void Show(Window owner)
{
this.Owner = owner;
this.Show();
}
Or overload a constructor:
public MyWindow(Window owner)
: this()
{
this.Owner = owner;
}
If you create an extention for this, you could reuse this fine idea:
/// <summary>
/// Opens a window modally, with an owner
/// </summary>
/// <param name="window">The window to open</param>
/// <param name="opener">The owner of the window getting opened</param>
/// <returns>window.ShowDialog()</returns>
public static bool? ShowDialog(this Window window, Window opener)
{
window.Owner = opener;
return window.ShowDialog();
}
In addition, we can use:
this.Owner = App.Current.MainWindow;
Or Application instead of App.
And place it in a child window constructor:
public partial class ChildWindow : Window
{
public ChildWindow()
{
InitializeComponent();
DataContext = new ChildWindowViewModel();
this.Owner = App.Current.MainWindow;
}
}
I had the same problem...but it was mostly due to the fact that, when i wanted to get rid of the child window, I used hide() instead of close() ... so when you reopen it, because it was hidden and not closed, when the parent window is moved, it still opens at it's startup location...
So when close the child window instead of hiding it for example when finished working with it.
Something else that can cause this is setting DataContext after InitializeComponent() is called.
If you have code-behind like this:
public CustomWindow(CustomViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
Change it to:
public CustomWindow(CustomViewModel viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
I am building a composite application using CAL/Prism. The main region is a tab control, with multiple types of views in it. Each view has a custom set commands that it can handle which are bound to toolbar buttons at the top of the window. I've done this before in non-CAL apps by simply setting the InputBinding on the command, but I haven't been able to find any such mechanism in the source code for the CAL modules.
My question is, what is the best way to hook up a keystroke to my view, so that when the user presses Alt + T, the associated DelegateCommand object handles it? Hooking up a shortcut can't be THAT difficult...
Just for reference, the CommandReference class is currently not included in an assembly that you can reference, but is included with the M-V-VM project template. So if you don't build your application from the template, then you have to get the class from somewhere else. I chose to copy it from the sample project. I included it below to allow everyone easy access to this little chunk of goodness, but be sure to check for updates to the template in future versions of the M-V-VM Toolkit.
/// <summary>
/// This class facilitates associating a key binding in XAML markup to a command
/// defined in a View Model by exposing a Command dependency property.
/// The class derives from Freezable to work around a limitation in WPF when data-binding from XAML.
/// </summary>
public class CommandReference : Freezable, ICommand
{
public CommandReference( )
{
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( "Command", typeof( ICommand ), typeof( CommandReference ), new PropertyMetadata( new PropertyChangedCallback( OnCommandChanged ) ) );
public ICommand Command
{
get { return (ICommand)GetValue( CommandProperty ); }
set { SetValue( CommandProperty, value ); }
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (Command != null)
return Command.CanExecute( parameter );
return false;
}
public void Execute(object parameter)
{
Command.Execute( parameter );
}
public event EventHandler CanExecuteChanged;
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
if (commandReference != null)
{
ICommand oldCommand = e.OldValue as ICommand;
if (oldCommand != null)
oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
ICommand newCommand = e.NewValue as ICommand;
if (newCommand != null)
newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
}
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore( )
{
return new CommandReference();
}
#endregion
}
Enjoy!
The MVVM Toolkit has a class called a CommandReference that will allow you to use a reference to a command as a keybinding.
<Window ...
xmlns:toolkit="clr-namespace:CannotRememberNamspace;assembly=OrTheAssembly"
>
<Window.Resources>
<toolkit:CommandReference
x:Key="ExitCommandReference"
Command="{Binding ExitCommand}" />
</Window.Resources>
<Window.InputBindings>
<KeyBinding Key="X"
Modifiers="Control"
Command="{StaticResource ExitCommandReference}" />
</Window.InputBindings>
</Window>
This'll do it.
Edit: Since this was written, WPF 4.0 fixed this particular issue and you no longer have to use the static resource workaround. You can reference the command in your viewmodel directly from the KeyBinding.
I'm trying to implement a WPF application using MVVM (Model-View-ViewModel) pattern and I'd like to have the View part in a separate assembly (an EXE) from the Model and ViewModel parts (a DLL).
The twist here is to keep the Model/ViewModel assembly clear of any WPF dependency. The reason for this is I'd like to reuse it from executables with different (non-WPF) UI techs, for example WinForms or GTK# under Mono.
By default, this can't be done, because ViewModel exposes one or more ICommands. But the ICommand type is defined in the System.Windows.Input namespace, which belongs to the WPF!
So, is there a way to satisfy the WPF binding mechanism without using ICommand?
Thanks!
You should be able to define a single WPF custom routed command in your wpf layer and a single command handler class. All your WPF classes can bind to this one command with appropriate parameters.
The handler class can then translate the command to your own custom command interface that you define yourself in your ViewModel layer and is independent of WPF.
The simplest example would be a wrapper to a void delegate with an Execute method.
All you different GUI layers simply need to translate from their native command types to your custom command types in one location.
WinForms doesn't have the rich data binding and commands infrastructure needed to use a MVVM style view model.
Just like you can't reuse a web application MVC controllers in a client application (at least not without creating mountains of wrappers and adapters that in the end just make it harder to write and debug code without providing any value to the customer) you can't reuse a WPF MVVM in a WinForms application.
I haven't used GTK# on a real project so I have no idea what it can or can't do but I suspect MVVM isn't the optimal approach for GTK# anyway.
Try to move as much of the behavior of the application into the model, have a view model that only exposes data from the model and calls into the model based on commands with no logic in the view model.
Then for WinForms just remove the view model and call the model from the UI directly, or create another intermediate layer that is based on WinForms more limited data binding support.
Repeat for GTK# or write MVC controllers and views to give the model a web front-end.
Don't try to force one technology into a usage pattern that is optimized for another, don't write your own commands infrastructure from scratch (I've done it before, not my most productive choice), use the best tools for each technology.
Sorry Dave but I didn't like your solution very much. Firstly you have to code the plumbing for each command manually in code, then you have to configure the CommandRouter to know about each view/viewmodel association in the application.
I took a different approach.
I have an Mvvm utility assembly (which has no WPF dependencies) and which I use in my viewmodel. In that assembly I declare a custom ICommand interface, and a DelegateCommand class that implements that interface.
namespace CommonUtil.Mvvm
{
using System;
public interface ICommand
{
void Execute(object parameter);
bool CanExecute(object parameter);
event EventHandler CanExecuteChanged;
}
public class DelegateCommand : ICommand
{
public DelegateCommand(Action<object> execute) : this(execute, null)
{
}
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public void Execute(object parameter)
{
_execute(parameter);
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
}
}
I also have a Wpf library assembly (which does reference the System WPF libraries), which I reference from my WPF UI project. In that assembly I declare a CommandWrapper class which has the standard System.Windows.Input.ICommand interface. CommandWrapper is constructed using an instance of my custom ICommand and simply delegates Execute, CanExecute and CanExecuteChanged directly to my custom ICommand type.
namespace WpfUtil
{
using System;
using System.Windows.Input;
public class CommandWrapper : ICommand
{
// Public.
public CommandWrapper(CommonUtil.Mvvm.ICommand source)
{
_source = source;
_source.CanExecuteChanged += OnSource_CanExecuteChanged;
CommandManager.RequerySuggested += OnCommandManager_RequerySuggested;
}
public void Execute(object parameter)
{
_source.Execute(parameter);
}
public bool CanExecute(object parameter)
{
return _source.CanExecute(parameter);
}
public event System.EventHandler CanExecuteChanged = delegate { };
// Implementation.
private void OnSource_CanExecuteChanged(object sender, EventArgs args)
{
CanExecuteChanged(sender, args);
}
private void OnCommandManager_RequerySuggested(object sender, EventArgs args)
{
CanExecuteChanged(sender, args);
}
private readonly CommonUtil.Mvvm.ICommand _source;
}
}
In my Wpf assembly I also create a ValueConverter that when passed an instance of my custom ICommand spits out an instance of the Windows.Input.ICommand compatible CommandWrapper.
namespace WpfUtil
{
using System;
using System.Globalization;
using System.Windows.Data;
public class CommandConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new CommandWrapper((CommonUtil.Mvvm.ICommand)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
}
Now my viewmodels can expose commands as instances of my custom command type without having to have any dependency on WPF, and my UI can bind Windows.Input.ICommand commands to those viewmodels using my ValueConverter like so. (XAML namespace spam ommited).
<Window x:Class="Project1.MainWindow">
<Window.Resources>
<wpf:CommandConverter x:Key="_commandConv"/>
</Window.Resources>
<Grid>
<Button Content="Button1" Command="{Binding CustomCommandOnViewModel,
Converter={StaticResource _commandConv}}"/>
</Grid>
</Window>
Now if I'm really lazy (which I am) and can't be bothered to have to manually apply the CommandConverter every time then in my Wpf assembly I can create my own Binding subclass like this:
namespace WpfUtil
{
using System.Windows.Data;
public class CommandBindingExtension : Binding
{
public CommandBindingExtension(string path) : base(path)
{
Converter = new CommandConverter();
}
}
}
So now I can bind to my custom command type even more simply like so:
<Window x:Class="Project1.MainWindow"
xmlns:wpf="clr-namespace:WpfUtil;assembly=WpfUtil">
<Window.Resources>
<wpf:CommandConverter x:Key="_commandConv"/>
</Window.Resources>
<Grid>
<Button Content="Button1" Command="{wpf:CommandBinding CustomCommandOnViewModel}"/>
</Grid>
</Window>
I needed an example of this so I wrote one using various techniques.
I had a few design goals in mind
1 - keep it simple
2 - absolutely no code-behind in the view (Window class)
3 - demonstrate a dependency of only the System reference in the ViewModel class library.
4 - keep the business logic in the ViewModel and route directly to the appropriate methods without writing a bunch of "stub" methods.
Here's the code...
App.xaml (no StartupUri is the only thing worth noting)
<Application
x:Class="WpfApplicationCleanSeparation.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
App.xaml.cs (load up the main view)
using System.Windows;
using WpfApplicationCleanSeparation.ViewModels;
namespace WpfApplicationCleanSeparation
{
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
var view = new MainView();
var viewModel = new MainViewModel();
view.InitializeComponent();
view.DataContext = viewModel;
CommandRouter.WireMainView(view, viewModel);
view.Show();
}
}
}
CommandRouter.cs (the magic)
using System.Windows.Input;
using WpfApplicationCleanSeparation.ViewModels;
namespace WpfApplicationCleanSeparation
{
public static class CommandRouter
{
static CommandRouter()
{
IncrementCounter = new RoutedCommand();
DecrementCounter = new RoutedCommand();
}
public static RoutedCommand IncrementCounter { get; private set; }
public static RoutedCommand DecrementCounter { get; private set; }
public static void WireMainView(MainView view, MainViewModel viewModel)
{
if (view == null || viewModel == null) return;
view.CommandBindings.Add(
new CommandBinding(
IncrementCounter,
(λ1, λ2) => viewModel.IncrementCounter(),
(λ1, λ2) =>
{
λ2.CanExecute = true;
λ2.Handled = true;
}));
view.CommandBindings.Add(
new CommandBinding(
DecrementCounter,
(λ1, λ2) => viewModel.DecrementCounter(),
(λ1, λ2) =>
{
λ2.CanExecute = true;
λ2.Handled = true;
}));
}
}
}
MainView.xaml (there is NO code-behind, literally deleted!)
<Window
x:Class="WpfApplicationCleanSeparation.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplicationCleanSeparation="clr-namespace:WpfApplicationCleanSeparation"
Title="MainWindow"
Height="100"
Width="100">
<StackPanel>
<TextBlock Text="{Binding Counter}"></TextBlock>
<Button Content="Decrement" Command="WpfApplicationCleanSeparation:CommandRouter.DecrementCounter"></Button>
<Button Content="Increment" Command="WpfApplicationCleanSeparation:CommandRouter.IncrementCounter"></Button>
</StackPanel>
</Window>
MainViewModel.cs (includes the actual Model as well since this example is so simplified, please excuse the derailing of the MVVM pattern.
using System.ComponentModel;
namespace WpfApplicationCleanSeparation.ViewModels
{
public class CounterModel
{
public int Data { get; private set; }
public void IncrementCounter()
{
Data++;
}
public void DecrementCounter()
{
Data--;
}
}
public class MainViewModel : INotifyPropertyChanged
{
private CounterModel Model { get; set; }
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public MainViewModel()
{
Model = new CounterModel();
}
public int Counter
{
get { return Model.Data; }
}
public void IncrementCounter()
{
Model.IncrementCounter();
PropertyChanged(this, new PropertyChangedEventArgs("Counter"));
}
public void DecrementCounter()
{
Model.DecrementCounter();
PropertyChanged(this, new PropertyChangedEventArgs("Counter"));
}
}
}
Just a quick and dirty and I hope it's useful to someone. I saw a few different approaches through various Google's but nothing was quite as simple and easy to implement with the least amount of code possible that I wanted. If there's a way to simplify even further please let me know, thanks.
Happy Coding :)
EDIT: To simplify my own code, you might find this useful for making the Adds into one-liners.
private static void Wire(this UIElement element, RoutedCommand command, Action action)
{
element.CommandBindings.Add(new CommandBinding(command, (sender, e) => action(), (sender, e) => { e.CanExecute = true; }));
}
Instead of the VM exposing commands, just expose methods. Then use attached behaviors to bind events to the methods, or if you need a command, use an ICommand that can delegate to these methods and create the command through attached behaviors.
Off course this is possible. You can create just another level of abstraction.
Add you own IMyCommand interface similar or same as ICommand and use that.
Take a look at my current MVVM solution that solves most of the issues you mentioned yet its completely abstracted from platform specific things and can be reused. Also i used no code-behind only binding with DelegateCommands that implement ICommand. Dialog is basically a View - a separate control that has its own ViewModel and it is shown from the ViewModel of the main screen but triggered from the UI via DelagateCommand binding.
See full Silverlight 4 solution here Modal dialogs with MVVM and Silverlight 4
I think you are separating your Project at wrong point. I think you should share your model and business logic classes only.
VM is an adaptation of model to suit WPF Views. I would keep VM simple and do just that.
I can't imagine forcing MVVM upon Winforms. OTOH having just model & bussiness logic, you can inject those directly into a Form if needed.
" you can't reuse a WPF MVVM in a WinForms application"
For this please see url http://waf.codeplex.com/ , i have used MVVM in Win Form, now whenver i would like to upgrade application's presentation from Win Form to WPF, it will be changed with no change in application logic,
But i have one issue with reusing ViewModel in Asp.net MVC, so i can make same Desktop win application in Web without or less change in Application logic..
Thanks...