I've got a problem using System.Windows.Interactivity.Interaction attached behavior class (from Expression Blend SDK 4). I'd like to define a pair of triggers for System.Windows.Window class in XAML Style element. But as the TriggersProperty field of System.Windows.Interactivity.Interaction class is private and there is no SetTriggers method in this class, I've got an error 'Set property System.Windows.Setter.Property threw an exception. -> Value cannot be null. Parameter name: property' when running the following code.
I really want to use the triggers and actions in styles, because I'd like to use them for my window-descendant control. Of course I can use my custom behavior or directly code my window-descendant class with triggers-analogue logic, but I'd like to use already existent triggers and actions of the expression library and my own, not declining them, simply because the TriggersProperty of Interaction class is hidden and I can't set it through style.
Is any workaround for the problem? With Reflection or someway other?
PS. I already tried to declare custom static class with TriggersProperty attached dependency property, registered with the help of AddOwner method, but no help - at the end it still tries to access the same TriggersProperty in the same System.Windows.Interactivity.Interaction class.
<Window
x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:windowsInteractivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<Window.Style>
<Style TargetType="Window">
<Setter Property="Title" Value="WindowStyleTest"/>
<Setter Property="windowsInteractivity:Interaction.Triggers">
<Setter.Value>
<windowsInteractivity:EventTrigger EventName="MouseDown"/>
</Setter.Value>
</Setter>
</Style>
</Window.Style>
</Window>
!!!Update!!!
Okay I took it a bit further. I extended the extension to perform all the work including setting the Triggers collection.
TriggerCollectionExtension
The Extension That does all the heavy lifting.
Note: The first time ProvideValue is called it will be from loading the style so the TargetValue is a Setter.
[ContentProperty("Triggers")]
public class TriggerCollectionExtension : MarkupExtension
{
public string EventName { get; set; }
public string CommandName { get; set; }
public object CommandParameter { get; set; }
public System.Windows.Interactivity.TriggerCollection Triggers { get; private set;}
public TriggerCollectionExtension()
{
var trigCollectionType =
typeof(System.Windows.Interactivity.TriggerCollection);
var triggers = (System.Windows.Interactivity.TriggerCollection)
trigCollectionType.GetConstructor(
BindingFlags. NonPublic | BindingFlags. Instance,
null, Type.EmptyTypes, null).Invoke (null);
// Cheat to get around this problem.
// must have IsFrozen set to false to modify
var methCreateCore = trigCollectionType.GetMethod("CreateInstanceCore",
BindingFlags.NonPublic | BindingFlags.Instance);
var cloneTriggers =
(System.Windows.Interactivity.TriggerCollection)
methCreateCore.Invoke(triggers, null);
this.Triggers = cloneTriggers;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var target = serviceProvider.GetService(typeof(IProvideValueTarget)) as
IProvideValueTarget;
// The first time this is called is when loading the style.
// At that point the TargetObject is of type Setter.
// Return this (The MarkupExtension) and it will be reevaluated when the style
// is applied.
var hostcontrol = target.TargetObject as Control;
if (hostcontrol != null)
{
var cloneTriggers = this.Triggers;
var eventTrigger = new EventTrigger(this.EventName);
var trigbase = eventTrigger as TriggerBase;
trigbase.Attach(hostcontrol);
var commandAction = new CommandAction(hostcontrol, this.CommandName,
this.CommandParameter);
eventTrigger.Actions.Add(commandAction);
cloneTriggers.Add(eventTrigger);
Interaction.SetShadowTriggers(hostcontrol, this.Triggers);
return null;
}
else
{
return this;
}
return null;
}
}
Interaction
The re-ownership/exposure of the TriggersCollection.
<!-- language: c# -->
/// <summary>
/// Helps workaround the bug in the deployed interaction DLL.
/// The DependencyProperty is registered as ShadowTriggers and the Setter Getter is
/// SetTriggers() GetTriggers().
/// The result is compile error for XAML if anything but Shadowtriggers is used and
/// runtime error.
/// </summary>
public static class Interaction
{
static Interaction()
{
var interActionType = typeof(System.Windows.Interactivity.Interaction);
var triggersProperty = (DependencyProperty)interActionType.InvokeMember(
"TriggersProperty",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetField,
null, null, null);
ShadowTriggersProperty = triggersProperty.AddOwner(typeof(Interaction));
}
public static readonly DependencyProperty ShadowTriggersProperty;
public static System.Windows.Interactivity.TriggerCollection
GetShadowTriggers(DependencyObject d)
{
return
(System.Windows.Interactivity.TriggerCollection)
d.GetValue(ShadowTriggersProperty);
}
public static void
SetShadowTriggers(
DependencyObject d,
System.Windows.Interactivity.TriggerCollection value)
{
d.SetValue(ShadowTriggersProperty, value);
}
}
CommandAction
A custom TriggerAction that looks up the Command on the DataContext.
<!-- language: c# -->
public class CommandAction : TriggerAction<FrameworkElement>
{
FrameworkElement control;
private string commandName;
object commandParameter;
private ICommand actualCommand;
public CommandAction(FrameworkElement control, string commandName,
object commandParameter)
{
this.control = control;
this.commandName = commandName;
this.commandParameter = commandParameter;
object datacontext;
if (this.FindDataContext(this.control, out datacontext))
{
var datacontextType = datacontext.GetType();
var propCommand = datacontextType.GetProperty(this.commandName);
this.actualCommand = propCommand.GetValue(datacontext, null) as ICommand;
}
}
private bool FindDataContext(FrameworkElement control, out object datacontext)
{
datacontext = default(object);
var parent = VisualTreeHelper.GetParent(control);
while (parent != null)
{
var parentFrame = parent as FrameworkElement;
if (parentFrame != null)
{
datacontext = parentFrame.DataContext;
if (datacontext != null)
{
return true;
}
}
var parentFrameContent = parent as FrameworkContentElement;
if (parentFrameContent != null)
{
datacontext = parentFrameContent.DataContext;
if (datacontext != null)
{
return true;
}
}
parent = VisualTreeHelper.GetParent(parent);
}
return false;
}
protected override void Invoke(object parameter)
{
if (this.actualCommand != null)
{
if (this.actualCommand.CanExecute(parameter))
{
this.actualCommand.Execute(parameter);
}
}
}
}
Wow long time reader first time posting code. I finally learned why the code doesn't always cut and paste so well. It took so many tries to submit this update.
I am sure there are reasons like disk space, parsing, or rendering speed, and the editor maintains state on failure to submit excellently.
I got it, why the error appears. That's because at runtime it's searching an Attached Dependency property by string name, that is "ShadowTriggers" (as it specified in System.Windows.Interactivity assembly, Interaction static constructor). So I created the own custom static class and inherit the Triggers Dependency Property from System.Windows.Interaction there (via Reflection and AddOwner, just exposed the property as ShadowTriggersProperty). It worked! But... Now I have to provide a TriggerCollection instance to the Style's Property Value Setter, and the constructor of the class is internal. Suppose it is a no way further.
Related
I have been reading Mark Seeman's book on dependency injection in .NET and I'm struggling to configure composition root in WPF application.
My container will be registered in the application startup method:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var container = new Container();
container.Configure(r =>
{
r.For<IAccountServices>().Use<AccountServicesProxy>();
r.For<MainWindow>().Use<MainWindow>();
});
}
This makes sense as the application startup represents my composition root.
WPF windows in my application are based on view models. View models use constructor injection. E.g. I may compose a view model by injecting implementation of IAccountServices.
When it comes to creating my main window, I can do the following inside of the OnStartup method:
var mainWindow = container.GetInstance<MainWindow>();
mainWindow.Show();
Once I'm inside of the main window, I might want open up another window. So far I've been able to come up with one way of doing this, which is to create a window factory and ask window factory to resolve instance of the window. I'll have to make sure that window factory is available in every view model that might need to open a new window. In my mind this is as bad as passing IoC container around my application (service locator anti-pattern comes to mind).
Does this approach seem right to you? My gut feeling tells me that this is wrong, but I haven't come up with a better way of achieving this (yet).
I think before implement patterns of behavior, such as a Mediator, and the like, need to decide on a generic pattern for easy application structure. For this purpose, namely, for the create independent windows, well suited Abstract factory pattern.
Creation of the windows can be implemented on the side ViewModel using methods such as IDialogService. But I think that this task should be implemented on the side View, because the Window object refers to the View and not to ViewModel. So, you must create MVVM style architecture that it allows create independent windows using design patterns.
I created a project in which an Abstract factory creates a Window on the side of the View using the attached behavior. Abstract factory also implements the Singleton pattern to create a global point of access and to ensure the uniqueness of the newly constructed object. Attached behavior implicitly implements pattern Decorator who is a wrapper for an abstract factory that is used on the side of XAML. To an Abstract factory does not refer to objects which are located in ViewModel is used a Proxy pattern which is a ContentControl with DataTemplate without DataType. Also used Command pattern for independent action between objects. As a result, this project uses the following patterns:
Abstract factory
Singleton
Decorator
Proxy
Command
The project structure looks like this:
In the attached behavior has attached dependency property Name, which is transmitted in the name of the new window. For him registered PropertyChangedEvent, which is a call Make method an abstract factory:
private static void IsFactoryStart(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var window = sender as Window;
if (window == null)
{
return;
}
if (e.NewValue is String && String.IsNullOrEmpty((string)e.NewValue) == false)
{
_typeWindow = (string)e.NewValue;
if (_typeWindow != null)
{
var newWindow = WindowFactory.Instance.Make(_typeWindow);
newWindow.Show();
}
}
}
WindowFactory together with the Singleton pattern looks like this:
public class WindowFactory : IWindowFactory
{
#region WindowFactory Singleton Instance
private static WindowFactory _instance = null;
private static readonly object padlock = new object();
public static WindowFactory Instance
{
get
{
lock (padlock)
{
if (_instance == null)
{
_instance = new WindowFactory();
}
return _instance;
}
}
}
#endregion
public Window Make(string TypeWindow)
{
if (TypeWindow.Equals("WindowOneViewProxy"))
{
var windowOne = new Window();
windowOne.Width = 450;
windowOne.Height = 250;
windowOne.WindowStartupLocation = WindowStartupLocation.CenterScreen;
windowOne.Title = TypeWindow;
windowOne.ContentTemplate = Application.Current.Resources[TypeWindow] as DataTemplate;
return windowOne;
}
else if (TypeWindow.Equals("WindowTwoViewProxy"))
{
var windowTwo = new Window();
windowTwo.Width = 500;
windowTwo.Height = 200;
windowTwo.WindowStartupLocation = WindowStartupLocation.CenterScreen;
windowTwo.Title = TypeWindow;
windowTwo.ContentTemplate = Application.Current.Resources[TypeWindow] as DataTemplate;
return windowTwo;
}
else if (TypeWindow.Equals("WindowThreeViewProxy"))
{
var windowThree = new Window();
windowThree.Width = 400;
windowThree.Height = 140;
windowThree.WindowStartupLocation = WindowStartupLocation.CenterScreen;
windowThree.Title = TypeWindow;
windowThree.ContentTemplate = Application.Current.Resources[TypeWindow] as DataTemplate;
return windowThree;
}
else
throw new Exception("Factory can not create a: {0}" + TypeWindow);
}
}
For the property Window.ContentTemplate set DataTemplate from resources. ContentTemplate is responsible for the visual representation, in order to bind properties from ViewModel, you need to set the object to Content. But in this case, the Abstract factory reference will to ViewModel, and to avoid them and using the proxy pattern as follows:
WindowOneProxyView
<DataTemplate x:Key="WindowOneViewProxy">
<ContentControl ContentTemplate="{StaticResource WindowOneViewRealObject}">
<ViewModels:WindowOneViewModel />
</ContentControl>
</DataTemplate>
WindowOneViewRealObject
<DataTemplate x:Key="WindowOneViewRealObject" DataType="{x:Type ViewModels:WindowOneViewModel}">
<Grid>
<Label Content="{Binding Path=WindowOneModel.TextContent}"
HorizontalAlignment="Center"
VerticalAlignment="Top"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Background="Beige" />
<Button Content="One command"
Width="100"
Height="30"
HorizontalAlignment="Center"
Command="{Binding OneCommand}" />
</Grid>
</DataTemplate>
In DataTemplate proxy is not specified DataType, but it is in the real object.
In MainViewModel has commands to simply set the window name, which will give input for attached behavior:
MainModel
public class MainModel : NotificationObject
{
#region TypeName
private string _typeName = null;
public string TypeName
{
get
{
return _typeName;
}
set
{
_typeName = value;
NotifyPropertyChanged("TypeName");
}
}
#endregion
}
MainViewModel
public class MainViewModel
{
#region MainModel
private MainModel _mainModel = null;
public MainModel MainModel
{
get
{
return _mainModel;
}
set
{
_mainModel = value;
}
}
#endregion
#region ShowWindowOneCommand
private ICommand _showWindowOneCommand = null;
public ICommand ShowWindowOneCommand
{
get
{
if (_showWindowOneCommand == null)
{
_showWindowOneCommand = new RelayCommand(param => this.ShowWindowOne(), null);
}
return _showWindowOneCommand;
}
}
private void ShowWindowOne()
{
MainModel.TypeName = "WindowOneViewProxy";
}
#endregion
#region ShowWindowTwoCommand
private ICommand _showWindowTwoCommand = null;
public ICommand ShowWindowTwoCommand
{
get
{
if (_showWindowTwoCommand == null)
{
_showWindowTwoCommand = new RelayCommand(param => this.ShowWindowTwo(), null);
}
return _showWindowTwoCommand;
}
}
private void ShowWindowTwo()
{
MainModel.TypeName = "WindowTwoViewProxy";
}
#endregion
#region ShowWindowThreeCommand
private ICommand _showWindowThreeCommand = null;
public ICommand ShowWindowThreeCommand
{
get
{
if (_showWindowThreeCommand == null)
{
_showWindowThreeCommand = new RelayCommand(param => this.ShowWindowThree(), null);
}
return _showWindowThreeCommand;
}
}
private void ShowWindowThree()
{
MainModel.TypeName = "WindowThreeViewProxy";
}
#endregion
public MainViewModel()
{
MainModel = new MainModel();
}
}
MainWindow looks as:
<Window x:Class="WindowFactoryNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:WindowFactoryNamespace.ViewModels"
xmlns:AttachedBehaviors="clr-namespace:WindowFactoryNamespace.AttachedBehaviors"
AttachedBehaviors:WindowFactoryBehavior.Name="{Binding Path=MainModel.TypeName}"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="300" Width="300">
<Window.DataContext>
<this:MainViewModel />
</Window.DataContext>
<WrapPanel>
<Button Content="WindowOne"
Margin="10"
Command="{Binding ShowWindowOneCommand}" />
<Button Content="WindowTwo"
Margin="10"
Command="{Binding ShowWindowTwoCommand}" />
<Button Content="WindowThree"
Margin="10"
Command="{Binding ShowWindowThreeCommand}" />
</WrapPanel>
</Window>
Test View-ViewModel for the first window looks like this (they practically identical):
WindowOneModel
public class WindowOneModel : NotificationObject
{
#region TextContent
private string _textContent = "Text content for WindowOneView";
public string TextContent
{
get
{
return _textContent;
}
set
{
_textContent = value;
NotifyPropertyChanged("TextContent");
}
}
#endregion
}
WindowOneViewModel
public class WindowOneViewModel
{
#region WindowOneModel
private WindowOneModel _windowOneModel = null;
public WindowOneModel WindowOneModel
{
get
{
return _windowOneModel;
}
set
{
_windowOneModel = value;
}
}
#endregion
#region OneCommand
private ICommand _oneCommand = null;
public ICommand OneCommand
{
get
{
if (_oneCommand == null)
{
_oneCommand = new RelayCommand(param => this.One(), null);
}
return _oneCommand;
}
}
private void One()
{
WindowOneModel.TextContent = "Command One change TextContent";
}
#endregion
public WindowOneViewModel()
{
WindowOneModel = new WindowOneModel();
}
}
This project is available at this link.
Output
MainWindow
WindowOne
WindowTwo
WindowThree
IMHO, there is no need to over complicate the solution for the sake of MVVM purity. You risk the subsequent developers not understanding your elegant solution and break it. In fact there is a good chance of that as "pure" implementations tend to be not that readable because of the complexity.
IMHO, any solution where a problem is permanently solved under an abstraction with minimal code overhead and simplicity in its usage is better than doing considerable overhead every time the solution is used even if "purity" is achieved(it won't serve any purpose). The problem of showing dialog in the application has to be solved once and it should be easy to use it in the future.
Composing view models is perfectly fine, and could make life easier by allowing view models to interact without drama
A dialog service can be created which will act as a wrapper for all your dialog needs in the application. You can inject the Dialog Service and the child view models which needs to be displayed in a window, to your parent view model. When you need to display the window, ask the Dialog service to do it, passing it the view model instance and view name.
Note:code is not complied or tested
public class DialogService : IDialogService
{
IEventAggregator _eventAggregator;
bool _fatalError;
//Provides a wrapper function which will connect your view and view model and open a
//dialog
public Window ShowCustomDialog<TViewModel>(string name, TViewModel viewModel, bool
modal, double left, double top, Action<bool?> OnClose, int width, int height)
{
if (_fatalError == true)
{
return null;
}
Window view = new Window(name);
if (viewModel != null)
{
view.DataContext = viewModel;
}
if (left != -1.0 && top != -1.0)
{
view.WindowStartupLocation = WindowStartupLocation.Manual;
view.Left = left;
view.Top = top;
}
else
{
view.WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
if (width != -1 && height != -1)
{
view.Width = width;
view.Height = height;
}
view.Closed += (o, e) =>
{
_eventAggregator.GetEvent<NotifyDialogAction>().Publish(false);
if (OnClose != null)
{
OnClose(e.DialogResult);
}
};
view.Loaded += (o, e) =>
{
_eventAggregator.GetEvent<NotifyDialogAction>().Publish(true);
Window window = o as Window;
if (window != null)
{
double dialogWidth = window.ActualWidth;
double screenWidth =
Application.Current.RootVisual.RenderSize.Width;
double dialogLeft = window.Left;
if (dialogLeft + dialogWidth > screenWidth)
{
window.Left = screenWidth - dialogWidth;
}
double dialogHeight = window.ActualHeight;
double screenHeight =
Application.Current.RootVisual.RenderSize.Height;
double dialogTop = window.Top;
if (dialogTop + dialogHeight > screenHeight)
{
window.Top = screenHeight - dialogHeight;
}
}
};
if (modal)
{
view.ShowDialog();
}
else
{
view.Show();
}
return view;
}
//Add more functions. For example to pop up a message box etc.
}
Usage
public class ComposedVM
{
public ViewModelA objA{get;set;}
public ViewModelB objB{get;set;}
IDialogService dialogService{get;set;}
public ComposedVM(ViewModelA a, ViewModelB b, IDialogService dlg )
{
objA = a;
objB = b;
dialogService = dlg
}
public void OnShowWindowACommand()
{
dialogService .ShowCustomDialog<object>(
DialogNames.ViewA/*view name constant*/, objA, true, -1.0, -1.0,
result =>
{
if (result == true)
{
dialogService.ShowMessageDialog(ApplicationStrings.SuccessFulOperation);
}
});
}
}
An event/message based communication can be used between modules. Using it for related view models in a module is an overkill IMHO.
Pushing container instance through constructor is a bad idea in 99% of cases, because container is a service locator. The main disadvantages of this approach are:
dependency from concrete implementation of container;
unclear API of your classes, which also leads to fragile unit tests.
There are many ways to create window in MVVM fashion:
using Mediators (like IMessenger in MvvmLight, IEventAggregator in Caliburn.Micro);
using special IDialogService;
using attached behaviours;
using Action that inserted via ViewModel constructor;
using Controllers.
I have a DataGrid witch is bound to a ObservableCollection<"Product">. The columns are bound to properties of Product. Most of then are of type double?(nullable).
In some time I have to set some property to null. After that, no matter the value I set, the binding don't work. The value is not updated in the view.
What happens to the binding when I set a property to null?
I tried what is shown in this blog post http://wildermuth.com/2009/11/18/Data_Binding_Changes_in_Silverlight_4 but it didn't worked to me.
Thanks!
Edit:
Below is the class I've created that implements the INotifyPropertyChanged
public class NotifyPropertyChangedAttribute : INotifyPropertyChanged
{
Dictionary<string, object> _propBag = new Dictionary<string, object>();
protected object Get(string propName)
{
object value = null;
_propBag.TryGetValue(propName, out value);
return value;
}
protected void Set(string propName, object value)
{
if (!_propBag.ContainsKey(propName) || Get(propName)!=null)
{
_propBag[propName] = value;
OnPropertyChanged(new PropertyChangedEventArgs(propName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
This is my Product class. The DataGrid's ItemsSource property is bound to a ObservableCollection of Products:
public class Product : NotifyPropertyChangedAttribute
{
public string Name
{
get { return (string)Get("Name") ?? ""; }
set { Set("Name", value); }
}
public double? Price
{
get {return (double)Get("Price") ?? null;}
set { Set("Price", value);}
}
public void Reset()
{
var propertyInfo = typeof(Product).GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
foreach (var p in propertyInfo)
{
p.SetValue(this , null, null);
}
}
}
Look the Reset() method. The binding stop working after I call this method.
In my app, I need that when the user press "Del" key, the DataGrid's row get empty, but can not be removed.
If you set the reference of the collection to null, the binding is broken between your control and source because the source doesn't exist anymore. In this case you have to explicitly rebind the items source in the control.
It is recommended to clear the collection instead of assigning null to it.
Update: For properties of items within the collection, make sure the item type implements INotifyPropertyChanged. The row in the DataGrid will be listening for changes through this interface on the item class itself.
I'm using L. Bugnion's MVVM Light Framework.
What are some of the recommended approaches to pass parameters such as Customer's ID to ViewModel's constructor?
Edit:
The parameter I need for each ViewModel is not something that is shared across models. it is something unique to each viewmodel instance.
//Create a container class to pass via messenger service
public class CarSelectedArgs
{
#region Declarations
public Car Car { get; set; }
#endregion
#region Constructor
public CarSelectedArgs(Car car)
{
Car = car;
}
#endregion
}
//example of view model sending message.
public class SendingViewModel : ViewModelBase
{
private Car _car;
public Car SelectedCar
{
get { return _car; }
set
{
_car = value;
if (value != null)
{
//messenger will notify all classes that have registered for a message of this type
Messenger.Default.Send(new CarSelectedArgs(value));
}
}
}
}
//Example of ViewModel registering to recieve a message
public class SampleViewModel : ViewModelBase
{
#region Constructor
public SampleViewModel()
{
Messenger.Default.Register<CarSelectedArgs>(this, OnCarSelected);
}
#endregion
#region LocalMethods
void OnCarSelected(CarSelectedArgs e)
{
var NewCar = e.Car;
}
#endregion
}
For me the whole point of using MVVM Light is to avoid injecting anything into the constructor of a View Model. MVVM Light provides a Messaging facility that allows you to send your parameters to a listener registered inside of the View Model.
For example, this is my View Model from my WordWalkingStick project using VSTO and WPF:
using System;
using System.Xml.Linq;
using GalaSoft.MvvmLight.Messaging;
namespace Songhay.Wpf.WordWalkingStick.ViewModels
{
using Songhay.Office2010.Word;
using Songhay.OpenXml;
using Songhay.OpenXml.Models;
using Songhay.Wpf.Mvvm;
using Songhay.Wpf.Mvvm.ViewModels;
/// <summary>
/// View Model for the default Client
/// </summary>
public class ClientViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ClientViewModel"/> class.
/// </summary>
public ClientViewModel()
{
if(base.IsInDesignMode)
{
#region
this._flatOpcSourceString = ApplicationUtility
.LoadResource(
new Uri("/Songhay.Wpf.WordWalkingStick;component/PackedFiles/FlatOpcToHtml.xml",
UriKind.Relative));
this._xhtmlSourceString = ApplicationUtility
.LoadResource(
new Uri("/Songhay.Wpf.WordWalkingStick;component/PackedFiles/FlatOpcToHtml.html",
UriKind.Relative));
#endregion
}
else
{
this._flatOpcSourceString = "Loading…";
this._xhtmlSourceString = "Loading…";
//Receive MvvmLight message:
Messenger.Default.Register(this,
new Action<GenericMessage<TransformationMessage>>(
message =>
{
var tempDocFolder =
Environment.ExpandEnvironmentVariables("%UserProfile%/Desktop/");
var inputPath = tempDocFolder + "temp.docx";
var outputPath = tempDocFolder + "temp.html";
var flatOpcDoc =
XDocument.Parse(message.Content.TransformationResult);
OpenXmlUtility.TransformFlatToOpc(flatOpcDoc, inputPath);
this.FlatOpcSourceString = flatOpcDoc.Root.ToString();
var settings = new SonghayHtmlConverterSettings()
{
PageTitle = "My Page Title " + DateTime.Now.ToString("U"),
UseEntityMap = false
};
OpenXmlUtility.WriteHtmlFile(inputPath, outputPath, settings);
var xhtmlDoc = XDocument.Load(outputPath);
this.XhtmlSourceString = xhtmlDoc.Root.ToString();
}));
}
}
/// <summary>
/// Gets or sets the flat opc source string.
/// </summary>
/// <value>The flat opc source string.</value>
public string FlatOpcSourceString
{
get
{
return _flatOpcSourceString;
}
set
{
_flatOpcSourceString = value;
base.RaisePropertyChanged("FlatOpcSourceString");
}
}
/// <summary>
/// Gets or sets the XHTML source string.
/// </summary>
/// <value>The XHTML source string.</value>
public string XhtmlSourceString
{
get
{
return _xhtmlSourceString;
}
set
{
_xhtmlSourceString = value;
base.RaisePropertyChanged("XhtmlSourceString");
}
}
string _flatOpcSourceString;
string _xhtmlSourceString;
}
}
You can see that MVVM Light is messaging (not injecting) values into the constructor (Messenger.Default.Register) with its Messenger.
Request anything you want, via injection, using interfaces.
If you have settings shared across models, instantiate a singleton containing the values and expose them via ISomethingProvider and ISomethingEditor interfaces.
Here is what I do:
ViewModel needs to show a car window with car id passed as parameter:
ViewModel -> message to codebehind for view to open window. Message sends id.
Essentially in code behind:
var vm = new viewmodel(id);
var view = new view();
view.datacontext = vm;
view.show();
my viewmodel has a constructor that takes in an id.
In the case of writing tests against the viewmodel I sometimes create an overload of the viewmodel constructor that takes an ISomething as a parameter. I have the default constructor call the second one with a default implementation of ISomething. In case of the test I call the constructor with a test implementation. I know it's not the best method, because it creates a dependency between the two classes... but sometimes you'll have to take the easy path...
public class SomeViewModel
{
private ISomething internalSomething;
public void SomeViewModel():this(new Something()){}
public void SomeViewModel(ISomething something)
{
this.internalSomething = something;
}
}
Update
Creating a view in xaml can be like this:
<UserControl xmlns="...."
xmlns:Example="SomeNamespace">
<UserControl.DataContext>
<Example:SomeViewModel />
</UserControl.DataContext>
<Grid>
...
</Grid>
</UserControl>
I have a class called MyComponent and it has a DependencyProperty caled BackgroundProperty.
public class MyComponent
{
public MyBackground Background
{
get { return (MyBackground)GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
public static readonly DependencyProperty BackgroundProperty =
DependencyProperty.Register("Background", typeof(MyBackground),
typeof(MyComponent), new FrameworkPropertyMetadata(default(MyBackground), new PropertyChangedCallback(OnPropertyChanged)));
}
MyBackground is a class that derives from DependencyObject and it has some DependencyProperties.
public class MyBackground : DependencyObject
{
public Color BaseColor
{
set { SetValue(BaseColorProperty, value); }
get { return (Color)GetValue(BaseColorProperty); }
}
public static readonly DependencyProperty BaseColorProperty =
DependencyProperty.Register("BaseColor", typeof(Color),
typeof(MyBackground ), new UIPropertyMetadata(Colors.White));
[...]
}
Now, what I want is when a property from MyBackground is changed, MyComponent to be notified that MyBackground has changed and the PropertyChangedCallback named OnPropertyChanged to be called.
Bear with me for a second because it appears that you are trying to go against the grain of WPF. Since it seems you are writing code related to display logic, the typical method for getting related DependencyObjects to interact with one another is through bindings.
If, for example, MyComponent is a control of some sort and it uses the Background property in its ControlTemplate, you would use a TemplateBinding that references the Background property and any important sub-properties.
Since 1) you probably already know that and 2) you either aren't using templates or don't have them available, you can set up a binding in code in order to react to changes in to the Background property. If you provide more detail about what your OnPropertyChanged method does I can provide some sample code.
One way to do what you describe would be to derive from Freezable instead of DependencyObject. When a property of a Freezable changes the PropertyChangedCallback for any DO referencing that Freezable will be invoked so the callback for the Background property of your MyComponent. In that case the e.OldValue and e.NewValue will be the same reference. Internally WPF has some flag on the event args that indicates that it is a subobject change.
This is what the framework does for things like brushes so that an element can be invalidated if say the Color property of a SolidColorBrush is changed. If an object will never be changed (or you want to make it thread safe) then one can freezing the object (i.e. making it immutable).
BTW I would probably avoid using Background as the name of the property. Most developers will assume that is of type Brush as that is what the framework uses for that named property on several of its elements (e.g. control, border).
Sounds like you want to use a DependencyPropertyDescriptor and AddValueChanged.
Here's an article on it: http://www.codeproject.com/Articles/34741/Change-Notification-for-Dependency-Properties.aspx
..and possibly a better implementation: http://agsmith.wordpress.com/2008/04/07/propertydescriptor-addvaluechanged-alternative/
Here's a small static class of extension methods I wrote for WPF -- it allows you to register an EventHandler or an Action callback for the changing of any DependencyProperty on any DependencyObject. No changes to the dependency object are necessary.
It also prevents recursion (i.e. if you change that same property during the callback, etc..)
It takes advantage of the DependencyPropertyDescriptor that #ScottBilas linked to.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
namespace BrainSlugs83.Writes.Too.Much.Code
{
public static class WpfExtensions
{
public static void OnPropertyChanged<T>(this T obj, DependencyProperty prop, Action<T> callback) where T : DependencyObject
{
if (callback != null)
{
obj.OnPropertyChanged(prop, new EventHandler((o, e) =>
{
callback((T)o);
}));
}
}
public static void OnPropertyChanged<T>(this T obj, DependencyProperty prop, EventHandler handler) where T : DependencyObject
{
var descriptor = DependencyPropertyDescriptor.FromProperty(prop, typeof(T));
descriptor.AddValueChanged(obj, new EventHandler((o, e) =>
{
if (handler != null)
{
if (o == null) { handler(o, e); }
else
{
lock (PreventRecursions)
{
if (IsRecursing(obj, prop)) { return; }
SetIsRecursing(obj, prop, true);
}
try
{
handler(o, e);
}
finally
{
SetIsRecursing(obj, prop, false);
}
}
}
}));
}
#region OnPropertyChanged Recursion Prevention
private static readonly Dictionary<object, List<DependencyProperty>> PreventRecursions = new Dictionary<object, List<DependencyProperty>>();
private static bool IsRecursing(object obj, DependencyProperty prop)
{
lock (PreventRecursions)
{
List<DependencyProperty> propList = null;
if (PreventRecursions.ContainsKey(obj))
{
propList = PreventRecursions[obj];
}
return propList == null ? false : propList.Contains(prop);
}
}
private static void SetIsRecursing(object obj, DependencyProperty prop, bool value)
{
lock (PreventRecursions)
{
List<DependencyProperty> propList = null;
if (PreventRecursions.ContainsKey(obj))
{
propList = PreventRecursions[obj];
}
if (propList == null)
{
if (!value) { return; }
propList = PreventRecursions[obj] = new List<DependencyProperty>();
}
if (value)
{
if (!propList.Contains(prop))
{
propList.Add(prop);
}
}
else
{
while (propList.Contains(prop))
{
propList.Remove(prop);
}
if (!propList.Any())
{
propList = PreventRecursions[obj] = null;
}
}
}
}
#endregion
public static bool IsInDesignMode(this DependencyObject obj)
{
try
{
return DesignerProperties.GetIsInDesignMode(obj);
}
catch { /* do nothing */ }
return false;
}
}
}
I'm currently struggling with one of the bindings I'm trying to add to my WPF project.
In the app I have a model with a bool property that cannot be used for databinding. Behind that property is a .NET remoting object that does some validation and writes the new value into the DB.
The requirement ist that the property should be displayed as checkbox, and as the user changes the value the new value should be immediatly provided to the .NET remoting object.
My approach so far:
I've created in my ViewModel with a DependencyProperty that is bound to my checkbox.
In the propertychanged handler of the DP, I'm writting the value to the property of the remoting object.
The problems I have with this approach:
if the validation within the .net remoting object raises an exception, this exception is swallowed. In addition the checkbox state and what's in the DB is not in sync. I tried to reset the value of the DP in case of an exception, but the checkbox doesn't reflect that.
What makes the situation even worse is the fact, that this WPF controls is integrated into an existing WinForms app.
So I would like to have the same behavior for these exceptions as I have implemented in my Application.ThreadException handler.
any ideas how to approach this?
The problem is that I heard only solutions for .NET 4.0 so far, but I'm working with 3.5SP1.
tia
Martin
Short demo code:
class TestVM : DependencyObject
{
private Model _m;
public TestVM()
{
_m = new Model();
}
public bool Value
{
get { return (bool)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof(bool),
typeof(TestVM),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
((sender, e) => ((TestVM)sender).Apply(e))));
private bool _suppress = false;
private void Apply(DependencyPropertyChangedEventArgs e)
{
if (_suppress) return;
try
{
_m.Value = this.Value;
}
catch
{
_suppress = true;
this.Value = _m.Value;
this.OnPropertyChanged(e);
}
finally
{
_suppress = false;
}
}
}
You don't need to use a DependencyObject as your ViewModel. You just need to implement INotifyPropertyChanged to get data binding support:
class TestVM
: INotifyPropertyChanged
{
private Model _m;
public TestVM()
{
_m = new Model();
}
public bool Value
{
get { return _m.Value; }
set
{
_m.Value = this.Value;
OnPropertyChanged("Value");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Note that if you expect the setter to throw exceptions, you may want to use an ExceptionValidationRule on the binding in your view.
Update: It sounds like your problem is that the Binding won't respond to PropertyChanged events within the call to set the source. One way to get around this is to use an asynchronous binding by setting IsAsync=True in the XAML for your binding. WPF will process the PropertyChanged event after it has finished updating the source value and won't think it is a reentrant call.
You can also get around this by using a Converter and turning off updates on PropertyChanged by doing UpdateSourceTrigger=LostFocus, but I don't think you would want that behavior.
I found a solution for my problem. I'm now deriving my own binding class that does the job.
public class ExceptionBinding : Binding
{
public ExceptionBinding(string name)
: base(name)
{
Construct();
}
public ExceptionBinding()
: base()
{
Construct();
}
private void Construct()
{
this.ValidatesOnExceptions = true;
this.UpdateSourceExceptionFilter = new UpdateSourceExceptionFilterCallback(OnException);
}
private object OnException(object bindExpression, Exception exception)
{
// ... custom error display ...
var exp = (BindingExpressionBase)bindExpression;
exp.UpdateTarget();
return null; // null needed to avoid display of the default error template
}
}