Binding to a field of a non-dependancy object - wpf

In my .NET 4.0 project I've got an object that has public fields and this object neither implements INotifyPropertyChanged nor inherits DependencyObject, and it will never do. However, I need a mechanism to "bind" to fields of this object in my WPF control. I know I can't do it directly as binding requires a dependency property (or at least, properties and notifying property changes), so what can I do to implement the binding functionality I need. I've tried something like this in my WPF control:
void FirePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public float Friction
{
get
{
if (CurrentObject != null)
{
return CurrentObject.Friction;
}
else
{
return 0.0f;
}
}
set
{
if (CurrentObject != null)
{
CurrentObject.Friction = value;
FirePropertyChanged("Friction");
}
}
}
public PlatformObjectTemplate CurrentObject
{
get
{
return GetValue(CurrentObjectProperty) as PlatformObjectTemplate;
}
set
{
SetValue(CurrentObjectProperty, value);
FirePropertyChanged("Friction");
FirePropertyChanged("CurrentObject");
BindShapes();
IntersectionComboBox.SelectedItem = CurrentObject.IntersectionStaticMethod;
}
}
public static readonly DependencyProperty CurrentObjectProperty = DependencyProperty.Register("CurrentObject", typeof(PlatformObjectTemplate), typeof(PlatformStaticObjectPropertyEditor), new PropertyMetadata(null));
My WPF control implements INotifyPropertyChanged, and my PlatformObjectTemplate does not have properties, just public fields like Friction. I need to bind to my object in XAML as such:
(in my control): //DoubleUpDown is from the WPF toolkit.
<tk:DoubleUpDown Margin="91,10,7,0" Name="doubleUpDown1" VerticalAlignment="Top" Value="{Binding Friction, ElementName=window, FallbackValue=0}" />
(in my main window):
<my:PlatformStaticObjectPropertyEditor x:Name="platformStaticObjectPropertyEditor1" CurrentObject="{Binding CurrentObject, ElementName=window}" />
I put a breakpoint in the getter of Friction property, and it tries to bind before the CurrentObject is bound, and because it is null, I can't read the correct friction value from the object. I've tried to fire Friction property changed in the setter of the CurrentObject, to populate the Friction when CurrentObject gets set, but that doesn't work either.
Ok, here are two requirements:
PlatformObjectTemplate will not use properties. It will have public fields.
I need a declarative way of binding as usual, just as I used in the XAML above.
I probably have got things over-complicated, and I must be missing some stuff. What is the most "correct" and "declarative" way of doing this right, within the constraints of my requirements just above?
Thanks,
Can.

object neither implements INotifyPropertyChanged nor inherits DependencyObject, and it will never. However, I need a mechanism to "bind" to fields of this object in my WPF control
poyra, I have this same situation. Because one cannot bind to instance fields, your best option is to create wrapper classes which implement INotifyPropertyChanged.

Related

Is DataBinding to a composite object's fields possible?

I have a WPF window with controls I wish to bind to my model. The model implements INotifyPropertyChanged to notify the view when the Properties change value. The Properties are primitives backed by fields, e.g:
private bool m_isRunning;
public bool IsRunning
{
get { return m_isRunning; }
private set
{
m_isRunning= value;
OnPropertyChanged("IsRunning");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (String.IsNullOrEmpty(propertyName))
{
return;
}
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
This works fine for primtiive values. I also have a composite object with various primitive properties and a hash table. Or, I'd like to bind to an ObservableCollection's Count property. I would like to bind my View controls to properties within the composite object, but I do not think this is possible. Something like:
<Run Text="{Binding Path=CompositeObject.SomeInnerProperty, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
This doesn't seem possible, but is there some other way? I feel like my only option is to expose properties that map in to the inner property of the composite object, but this seems like a lot of repetiion, e.g.:
public bool SomeInnerProperty
{
get { return m_myComposite.SomeInnerProperty; }
private set
{
m_myComposite.SomeInnerProperty= value;
OnPropertyChanged("SomeInnerProperty");
}
}
There's nothing wrong with binding to something like CompositeObject.SomeInnerProperty, however if CompositeObject does not implement INotifyPropertyChanged, then your UI won't get notified of the change and know that it needs to update when SomeInnerProperty changes.
Also, note that you can only bind to properties (with get and set methods), and not fields. So you can bind to public string SomeValue { get; set; } but you can't bind to public string SomeValue;
In regards dealing with repetitive code, I personally use some Visual Studio macros to write my public properties for me, so perhaps you could look into doing something like that if you don't want to implement INotifyPropertyChanged on your CompositeObject class. It should be noted that Macros were removed from Visual Studio 2012 though, so if you have a newer version you might need to use some other alternative like creating an add-in to run your macros
I had this same problem some time ago. Look at how I solved it:
MVVM INotifyPropertyChanged conflict with base class PropertyChange
Basically I created a Base class that implemented INotifyPropertyChanged and I made all my classes inherit from that base class and data binding worked fine.
You have two options:
If your model classes do not implement INPC, then create wrapper properties in your ViewModel like you suggested, or
Implement INPC in your model and just expose your main object in the ViewModel, you can bind as deep as you want as long as inner properties notify changes.

Updating a dependency property based on changes in the view model

I'm having some problems with data binding in WPF. Here's the scenario: I have made a user control which simulates a Dial Pad (i.e., an array of 12 buttons with the digits from '0' to '9' plus the '#' and 'Clear' keys). The control lives inside a class library and it's been implemented following the MVVM pattern, mainly because I need the components in the class library to be easily unit tested.
The view model for the control is quite simple, it basically updates a public "DialedNumber" string (which is internally connected to the model) every time the user presses a dial pad key button. The binding is working correctly and, by using the debugger, I can confirm that the "DialedNumber" variable inside the viewmodel is getting updated as I press button in the dial pad.
This DialPad control is used by a separate XAML file (Panel.xaml), which laids out several controls that belong to my custom class library.
Now, I'd like to add a TextBlock inside my Panel file in order to display the "DialedNumber" string held inside the DialPad. This is the code snippet in Panel.xaml:
<PanelControls:DialPad x:Name="MyDialPad" DialedNumber="55325"/>
<TextBlock Text="{Binding ElementName=MyDialPad, Path=DialedNumber}" />
The result I'm getting is that the textblock displays the correct number on start (i.e., "55325"), but its content doesn't get updated as I press the dial pad keys (even though the DialPad's viewmodel gets updated as I press new keys, as I've checked with the debugger).
Here's the code behind for the DialPad view:
public partial class DialPad : UserControl
{
public DialPad()
{
InitializeComponent();
DataContext = new DialPadViewModel();
}
public void DialedNumberChanged(object sender, EventArgs e)
{
return;
}
public DialPadViewModel DialPadViewModel
{
get { return DataContext as DialPadViewModel; }
}
public string DialedNumber
{
get
{
var dialPadViewModel = Resources["DialPadVM"] as DialPadViewModel;
return (dialPadViewModel != null) ? dialPadViewModel.DialedNumber : "";
}
set
{
var dialPadViewModel = Resources["DialPadVM"] as DialPadViewModel;
if (dialPadViewModel != null)
{
dialPadViewModel.DialedNumber = value;
}
}
}
}
Here's the DialPad view model:
public class DialPadViewModel : ObservableObject
{
public DialPadViewModel()
{
_dialPadModel = new DialPadModel();
}
#region Fields
private readonly DialPadModel _dialPadModel;
private ICommand _dialPadKeyPressed;
#endregion
#region Public Properties/Command
public DialPadModel DialPadModel
{
get { return _dialPadModel; }
}
public ICommand DialPadKeyPressedCommand
{
get
{
if (_dialPadKeyPressed == null)
{
_dialPadKeyPressed = new RelayCommand(DialPadKeyPressedCmd);
}
return _dialPadKeyPressed;
}
}
public string DialedNumber
{
get { return _dialPadModel.DialedNumber; }
set
{
_dialPadModel.DialedNumber = value;
RaisePropertyChanged("DialedNumber");
}
}
#endregion
#region Private Helpers
private void DialPadKeyPressedCmd(object parameter)
{
string keyPressedString = parameter.ToString();
if (keyPressedString.Length > 0)
{
if (char.IsDigit(keyPressedString[0]))
{
DialedNumber += keyPressedString[0].ToString(CultureInfo.InvariantCulture);
}
else if (keyPressedString == "C" || keyPressedString == "Clr" || keyPressedString == "Clear")
{
DialedNumber = "";
}
}
}
#endregion
}
Let me restate my problem: the textblock in Panel.xaml displays the correct number (55325) on start, but its value never gets updated as I press the DialPadButtons. I've placed a breakpoint inside DialPadKeyPressedCmd and I can confirm that the method gets executed everytime I press a key in the dial pad.
DependencyProperties are meant to point to some other property to get their value. So you can either point it to your DialPadViewModel.DialedNumber, or you can point it to some other string when the UserControl is used (either a binding or a hardcoded value like "551"), but you can't do both.
In your case, when someone binds to the DialedNumber dependency property, they are replacing the current value (the binding to DialPadViewModel.DialedNumber) with a new value.
Depending on how your code looks and what you want to do, there are a few ways around it.
First, you could insist that people who want to use your control also use your ViewModel, and don't make DialedNumber a public dependency property.
So instead of being allowed to create a custom class with a property of SomeOtherDialedNumber and binding
<DialPad DialedNumber="{Binding SomeOtherDialedNumber}">
they are forced to use the DialPadViewModel in their code anytime they want to use the DialPad control. For this to work, you would need to remove the this.DataContext = new DialPadViewModel in your code-behind the UserControl since the user will be providing the DialPadViewModel to your UserControl, and you can use an implicit DataTemplate to tell WPF to always draw DialPadViewModel with your DialPad UserControl.
<DataTemplate DataType="{x:Type DialPadViewModel}">
<local:DialPad />
</DataTemplate>
The other alternative I can think of is to synchronize your DependencyProperty with your ViewModel property with some PropertyChange notifications.
You would need to update DialPadViewModel.DialedNumber anytime the DialedNumber dependency property changes (You may need to use DependencyPropertyDescriptor.AddValueChanged for property change notification), and you would also have to write something to update the source of the DialedNumber dependency property anytime DialPadViewModel.DialedNumber changes.
Personally, if my UserControl has a ViewModel then I use the first option. If not, I get rid of the ViewModel entirely and build the logic for my UserControl in the code-behind, without a ViewModel.
The reason for this is that WPF works with two layers: a UI layer and a data layer. The DataContext is the data layer, and a ViewModel is typically part of the data layer. By setting the data layer (DataContext) explicitly in the UserControl's constructor, you are combining your data layer with your UI layer, which goes against one of the biggest reasons for using MVVM: separation of concerns. A UserControl should really just be a pretty shell only, and you should be able to place it on top of any data layer you want.
If you place your DialPad in your View, you can create a DialPadViewModel-Property (public+global) in your ViewViewModel:
public DialPadViewModel DialPadViewModel = new DialPadViewModel();
Now set the DataContext-Binding of your View to the ViewViewModel and bind the DialPads DataContext also to it, like
<local:DialPad DataContext="{Binding}"/>
Now you can bind to the properties in your DialPadViewModel:
<TextBox Text="{Binding Path=DialPadViewModel.DialedNumber}"/>
Thats how you can Access your DialPadViewModel from your View and your DialPad.
EDIT:
Now try changing your DialedNumber Property in your DialPad.xaml.cs like this:
public string DialedNumber
{
get
{
return DialPadViewModel.DialedNumber;
}
set
{
DialPadViewModel.DialedNumber = value;
}
}
EDIT 2: I found the Problem:
In your DialPad.xaml all your Commands were bound to the DialPadViewModel from the resources, while the TextBloc was bound to the DialPads DataContext, which is another instance of the DialPadViewModel.
So everytime you hit a DialPad-Button you changed the value of the DialedNumber from the resources' DPVM-instance not the DialedNumber from the DataContext's DPVM-instance.
It sounds like you can add a TextBox to your view and bind it's Text property to your view-model's DialedNumber property.
<TextBox Text="{Binding Path=DialedNumber}"></TextBox>
Your view-model property can look something like this:
private string _dialedNumber;
[DefaultValue("551")]
public string DialedNumber
{
get { return _dialedNumber; }
set
{
if (value == _dialedNumber)
return;
_dialedNumber= value;
_yourModel.DialedNumber= _dialedNumber;
this.RaisePropertyChanged("DialedNumber");
}
}
Let me know if I misunderstood your question.

MVVM pattern violation: MediaElement.Play()

I understand that ViewModel shouldn't have any knowledge of View, but how can I call MediaElement.Play() method from ViewModel, other than having a reference to View (or directly to MediaElement) in ViewModel?
Other (linked) question: how can I manage View's controls visibility from ViewModel without violating MVVM pattern?
1) Do not call Play() from the view model. Raise an event in the view model instead (for instance PlayRequested) and listen to this event in the view:
view model:
public event EventHandler PlayRequested;
...
if (this.PlayRequested != null)
{
this.PlayRequested(this, EventArgs.Empty);
}
view:
ViewModel vm = new ViewModel();
this.DataContext = vm;
vm.PlayRequested += (sender, e) =>
{
this.myMediaElement.Play();
};
2) You can expose in the view model a public boolean property, and bind the Visibility property of your controls to this property. As Visibility is of type Visibility and not bool, you'll have to use a converter.
You can find a basic implementation of such a converter here.
This related question might help you too.
For all the late-comers,
There are many ways to achieve the same result and it really depends on how you would like to implement yours, as long as your code is not difficult to maintain, I do believe it's ok to break the MVVM pattern under certain cases.
But having said that, I also believe there is always way to do this within the pattern, and the following is one of them just in case if anyone would like to know what other alternatives are available.
The Tasks:
we don't want to have direct reference from the ViewModel to any UI elements, i.e. the the MediaElement and the View itself.
we want to use Command to do the magic here
The Solution:
In short, we are going to introduce an interface between the View and the ViewModel to break the dependecy, and the View will be implementing the interface and be responsible for the direct controlling of the MediaElement while leaving the ViewModel talking only to the interface, which can be swapped with other implementation for testing purposes if needed, and here comes the long version:
Introduce an interface called IMediaService as below:
public interface IMediaService
{
void Play();
void Pause();
void Stop();
void Rewind();
void FastForward();
}
Implement the IMediaService in the View:
public partial class DemoView : UserControl, IMediaService
{
public DemoView()
{
InitializeComponent();
}
void IMediaService.FastForward()
{
this.MediaPlayer.Position += TimeSpan.FromSeconds(10);
}
void IMediaService.Pause()
{
this.MediaPlayer.Pause();
}
void IMediaService.Play()
{
this.MediaPlayer.Play();
}
void IMediaService.Rewind()
{
this.MediaPlayer.Position -= TimeSpan.FromSeconds(10);
}
void IMediaService.Stop()
{
this.MediaPlayer.Stop();
}
}
we then do few things in the DemoView.XAML:
Give the MediaElement a name so the code behind can access it like above:
<MediaElement Source="{Binding CurrentMedia}" x:Name="MediaPlayer"/>
Give the view a name so we can pass it as a parameter, and
import the interactivity namespace for later use (some default namespaces are omitted for simplicity reason):
<UserControl x:Class="Test.DemoView"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
x:Name="MediaService">
Hookup the Loaded event through Trigger to pass the view itself to the view model through a Command
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=MediaService}"></ia:InvokeCommandAction>
</ia:EventTrigger>
</ia:Interaction.Triggers>
last but not least, we need to hookup the media controls through Commands:
<Button Command="{Binding PlayCommand}" Content="Play"></Button>
<Button Command="{Binding PauseCommand}" Content="Pause"></Button>
<Button Command="{Binding StopCommand}" Content="Stop"></Button>
<Button Command="{Binding RewindCommand}" Content="Rewind"></Button>
<Button Command="{Binding FastForwardCommand}" Content="FastForward"></Button>
We now can catch everything in the ViewModel (I'm using prism's DelegateCommand here):
public class AboutUsViewModel : SkinTalkViewModelBase, IConfirmNavigationRequest
{
public IMediaService {get; private set;}
private DelegateCommand<IMediaService> loadedCommand;
public DelegateCommand<IMediaService> LoadedCommand
{
get
{
if (this.loadedCommand == null)
{
this.loadedCommand = new DelegateCommand<IMediaService>((mediaService) =>
{
this.MediaService = mediaService;
});
}
return loadedCommand;
}
}
private DelegateCommand playCommand;
public DelegateCommand PlayCommand
{
get
{
if (this.playCommand == null)
{
this.playCommand = new DelegateCommand(() =>
{
this.MediaService.Play();
});
}
return playCommand;
}
}
.
. // other commands are not listed, but you get the idea
.
}
Side note: I use Prism's Auto Wiring feature to link up the View and ViewModel. So at the View's code behind file there is no DataContext assignment code, and I prefer to keep it that way, and hence I chose to use purely Commands to achieve this result.
I use media element to play sounds in UI whenever an event occurs in the application. The view model handling this, was created with a Source property of type Uri (with notify property changed, but you already know you need that to notify UI).
All you have to do whenever source changes (and this is up to you), is to set the source property to null (this is why Source property should be Uri and not string, MediaElement will naturally throw exception, NotSupportedException I think), then set it to whatever URI you want.
Probably, the most important aspect of this tip is that you have to set MediaElement's property LoadedBehaviour to Play in XAML of your view. Hopefully no code behind is needed for what you want to achieve.
The trick is extremely simple so I won't post a complete example. The view model's play function should look like this:
private void PlaySomething(string fileUri)
{
if (string.IsNullOrWhiteSpace(fileUri))
return;
// HACK for MediaElement: to force it to play a new source, set source to null then put the real source URI.
this.Source = null;
this.Source = new Uri(fileUri);
}
Here is the Source property, nothing special about it:
#region Source property
/// <summary>
/// Stores Source value.
/// </summary>
private Uri _Source = null;
/// <summary>
/// Gets or sets file URI to play.
/// </summary>
public Uri Source
{
get { return this._Source; }
private set
{
if (this._Source != value)
{
this._Source = value;
this.RaisePropertyChanged("Source");
}
}
}
#endregion Source property
As for Visibility, and stuff like this, you can use converters (e.g. from bool to visibility, which you can find on CodePlex for WPF, SL, WP7,8) and bind your control's property to that of the view model's (e.g. IsVisible). This way, you control parts of you view's aspect. Or you can just have Visibility property typed System.Windows.Visibility on your view model (I don't see any pattern breach here). Really, it's not that uncommon.
Good luck,
Andrei
P.S. I have to mention that .NET 4.5 is the version where I tested this, but I think it should work on other versions as well.

DependencyProperty getter/setter not being called

I am trying to create a Custom control derived from a standard Grid.
I added a ObservableCollection as a DependencyProperty of the Custom control. However, the get/set of it is never reached. Can I have some guidelines in creating a DependencyProperty that works correctly with and ObservableCollection?
public class MyGrid : Grid
{
public ObservableCollection<string> Items
{
get
{
return (ObservableCollection<string>)GetValue(ItemsProperty);
}
set
{
SetValue(ItemsProperty, value);
}
}
public static DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ObservableCollection<string>),
typeof(MyGrid), new UIPropertyMetadata(null, OnItemsChanged));
}
I would suggest not to use ObservableCollection as the type of an Items dependency property.
The reason for having an ObservableCollection here (I guess) is to enable the UserControl to attach a CollectionChanged handler when the property value is assigned. But ObservableCollection is too specific.
The approach in WPF (e.g. in ItemsControl.ItemsSource) is to define a very basic interface type (like IEnumerable) and when the property is assigned a value, find out if the value collection implements certain more specific interfaces. This would at least be INotifyCollectionChanged here, but the collection might also implement ICollectionView and INotifyPropertyChanged. All these interfaces wouldn't be mandatory and that would enable your dependency property to bind to all sorts of collections, starting with a plain array up to a complex ItemCollection.
Your OnItemsChanged property change callback would then look like this:
private static void OnItemsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MyGrid grid = obj as MyGrid;
if (grid != null)
{
var oldCollectionChanged = e.OldValue as INotifyCollectionChanged;
var newCollectionChanged = e.NewValue as INotifyCollectionChanged;
if (oldCollectionChanged != null)
{
oldCollectionChanged.CollectionChanged -= grid.OnItemsCollectionChanged;
}
if (newCollectionChanged != null)
{
newCollectionChanged.CollectionChanged += grid.OnItemsCollectionChanged;
// in addition to adding a CollectionChanged handler
// any already existing collection elements should be processed here
}
}
}
private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// handle collection changes here
}
The WPF binding mechanism may bypass your standard CLR property and go directly to the dependency property accessors (GetValue and SetValue).
That is why logic should not be placed inside of the CLR property, but instead inside a changed handler.
Also the ObservableCollection<string> will never be set because when you use collection properties from XAML, like the following:
<local:MyGrid>
<local:MyGrid.Items>
<sys:String>First Item</sys:String>
<sys:String>Second Item</sys:String>
</local:MyGrid.Items>
</local:MyGrid>
It is actually calling a get on Items and then calling Add for each of the elements.

MvvM with custom View-Elements. Data Binding problems

As a newbie in Data-Binding, I don't know what I am doing wrong.
I have some GUI elements defined in XAML, and I have data-binded them with appropriate ViewModels. So far so good.
I also have some custom elements (geometrical shapes) that I place inside a Canvas (which Canvas I place inside the mainwindow through a user-control). I derived these entities from FrameworkElement, to have support for data-binding.
So what I have done is to register some DependencyProperties and set the bindings to one of the existing ViewModels, as it seemed to me logical.
Now the DependencyProperties of these custom classes, display some strange behaviour.
i) When I keep the focus only on the Views (controls) that use the same ViewModel with the custom elements, the properties update normally. If I click everywhere else, the bindings break.
ii) Sometimes, the Callback wasn't called although the property was changing.
iii) When the StartupURI in App.xaml was the MainWindow, if I declared the ProfileV as a field (no matter where it was instantiated), the databinding mechanism worked in the way of (i). If it was declared inside the constructor, the mechanism didn't worked.
What I am doing wrong, and what crucial thing i misunderstand about databinding ??
class ProfileV : FrameworkElement, IGraphicalElement
{
public int SelectedTab
{
get { return (int)GetValue(SelectedTabProperty); }
set { SetValue(SelectedTabProperty, value); }
}
public static readonly DependencyProperty SelectedTabProperty =
DependencyProperty.Register("SelectedTab", typeof(int), typeof(ProfileV),
new PropertyMetadata(new PropertyChangedCallback(CallBack)));
public ProfileV(GeneralExecutionVM VM,CanvasV canvasV)
{
DataContext = VM;
BindingOperations.SetBinding(this, SelectedTabProperty, new Binding("SelectedTab"));
}
public static void CallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
Which binds with this
public class GeneralExecutionVM : ObservableObject
{
private int _SelectedTab;
public int SelectedTab
{
get { return _SelectedTab; }
set
{
if (_SelectedTab == value) return;
_SelectedTab = value;
base.RaisePropertyChanged("SelectedTab");
}
}
}
(Observable Object, is the base class from the MVVM Foundation, of Josh Smith.)
ANSWERED
OK i found it. The misconception here is about the DataContext.
Be careful when and where you set it, against setting an explicit source object. I misused it here, and caused a small chaotic situation.

Resources