I've got a ListView which is bound to a list of objects. When I select an item in the ListView, I catch a SelectionChanged event and then pass the selected object off to a details view.
protected void list_selectionChanged(object sender, SelectionChangedEventArgs e) {
var myObject = theList.SelectedItem as MyObjectType;
detailsView.DataContext = myObject;
}
detailsView is a UserControl in the same WPF as the ListView. It contains some XAML like so:
<Label Content="{Binding Path=deviceId}"></Label>
<l:MyUc deviceId="{Binding Path=deviceId}" />
Inside MyUC, I've got a DependencyProperty defined:
public static readonly DependencyProperty deviceIdProperty = DependencyProperty.Register("deviceId", typeof(Guid), typeof(MyUC), new FrameworkPropertyMetadata(null));
public Guid deviceId {
get { return (Guid)GetValue(deviceIdProperty); }
set { SetValue(deviceIdProperty, value); }
}
The Label shows the deviceId, but the property inside MyUC never gets set.
Can anyone spot my mistake?
When you use a Dependency Property in XAML, the set method never gets called. If you want to "see" this set, you need to add a property changed callback, as the binding mechanism directly sets the dependency property without your setter.
For details on how to implement this, see the PropertyChanged callbacks section of MSDN.
First, it would be helpful if you could add the actual XAML code where you define the ListView and it's properties.
Second, you should look at the output console (in Visual Studio debug session of course) and see whether there are binding errors regarding the bindings you defined.
It is very probable that the bindings provide values that does not fit the deviceId dependency property type and thus it never changes.
Related
I have a listview inside user control, and the user control is called on a page. I want to decide what will be the SelectionMode of the ListView from my page. To achieve this I have created a dependency property inside user control like this
public ListViewSelectionMode SelectionMode
{
get { return (ListViewSelectionMode)GetValue(SelectionModeProperty); }
set { SetValue(SelectionModeProperty, value); }
}
public static readonly DependencyProperty SelectionModeProperty =
DependencyProperty.Register("SelectionMode", typeof(ListViewSelectionMode), typeof(EditMultiSelectComboBoxControl), new PropertyMetadata(null));
XAML
<ListView SelectionMode="{x:Bind lvSelectionMode}"></ListView>
And on my main page, I am initializing this user control like this:
<local:myuc Loaded="UC_Loaded" ></local:myuc>
Code behind
private void UC_Loaded(object sender, RoutedEventArgs e)
{
MultiCombo.SelectionMode = ListViewSelectionMode.Multiple;
}
All looks good to me, but when I run the project no item inside Listview is clickable / selectable. What I am missing here?
It is recommended that you use OneWay binding mode, so that after loading, changes to control dependent properties will still be reflected on the UI
<ListView SelectionMode="{x:Bind lvSelectionMode, Mode=OneWay}"></ListView>
Thanks.
I have a user control with a dependency property:
public ObservableCollection<Exclusion> SelectedExclusions
{
get
{
return (ObservableCollection<Exclusion>)GetValue(SelectedExclusionsProperty);
}
set
{
SetValue(SelectedExclusionsProperty, value);
}
}
public static readonly DependencyProperty SelectedExclusionsProperty =
DependencyProperty.Register(nameof(TimeSeriesChart.SelectedExclusions),
typeof(ObservableCollection<Exclusion>),
typeof(TimeSeriesChart),
new PropertyMetadata(default(ObservableCollection<Exclusion>)));
I am adding a selected exclusion to this collection on key down:
protected override void OnKeyDown(KeyEventArgs e)
{
if(e.Key == Key.Delete)
{
this.SelectedExclusions.Add(this.ExclusionProviders[0].Exclusions[this.hitTestInfo.DataSeriesIndex]);
}
}
In the view model I have this property & backing variable:
private ObservableCollection<TimeSeriesLibraryInterop.Exclusion> selectedExclusionsToDelete = new ObservableCollection<TimeSeriesLibraryInterop.Exclusion>();
public ObservableCollection<TimeSeriesLibraryInterop.Exclusion> SelectedExclusionsToDelete
{
get
{
return this.selectedExclusionsToDelete;
}
set
{
this.selectedExclusionsToDelete = value;
this.RaisePropertyChanged();
}
}
Finally the binding in the view:
<userControl1 SelectedExclusions="{Binding SelectedExclusionsToDelete, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The dependency property collection is initialised and populated however the view model property setter is never hit when the dependency property collection changes (Add). I have no binding errors in the output window. Is there something I'm missing here?
Looks like you're adding an item to the collection rather than replacing the collection. You won't hit the vm collection property's setter that way.
If you want to your viewmodel to respond to items being added to the SelectedExclusionsToDelete collection, the viewmodel will need to handle the SelectedExclusionsToDelete.CollectionChanged event. "Properly" handling that event (remove, add, move, clear, etc.) is a real hassle, but if it's not a giant collection you can often get away with something quick and dirty: Treat any change as a whole new collection. I think that's exactly the case you've got, too.
Alternatively, for an even quicker and dirtier approach, I think you could make it a two-way binding by default and have the control assign a new ObservableCollection to this.SelectedExclusions in OnKeyDown. The binding will pass it back to the viewmodel and hit the setter.
I say proper because all the examples I have seen all seem to contradict each other and or fall short in some respects. I need to be able to bind a class object to my DP from XAML or set it programmatically in cb:
<local:MyControl MyDP="{Binding MyObject}"/>
or
MyControl mycontrol = new MyControl(){MyDP = MyObject};
and then within the control, two way bind elements to properties of the binding object:
<TextBox text="{Binding MyDP.text, ElementName=MyControl}"/>
I would think this is pretty standard stuff, but the lack of cohesion I have seen in people trying to write examples has led me to believe otherwise.
This is how I do it:
Assume a parent control that contains a status bar (User Control) it's markup looks like this:
<ContentControl x:Name="XFoot" Grid.Row="1" Grid.ColumnSpan="3" >
<UserControls:UCStatusBar />
</ContentControl>
Now in the UserControl named status bar it's dependency property looks like this:
public StatusUpdate CurrentStatus
{
get { return (StatusUpdate)GetValue(CurrentStatusProperty); }
set { SetValue(CurrentStatusProperty, value); }
}
// Using a DependencyProperty as the backing store for CurrentStatus. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CurrentStatusProperty =
DependencyProperty.Register("CurrentStatus", typeof(StatusUpdate), typeof(UCStatusBar),
new PropertyMetadata(
new StatusUpdate() { ErrorMessage="", IsIndeterminate=false, Status="Ready"}
)
);
Status Update looks like this, it's just a container for three properties shown in the status bar.
public class StatusUpdate
{
public StatusUpdate()
{
Status = "";
ErrorMessage = "";
IsIndeterminate = true;
}
public string Status { get; set; }
public string ErrorMessage { get; set; }
public bool IsIndeterminate { get; set; }
}
}
Anyone can update the status bar by accessing the CurrentStatus property of the statusbar. NOTE to make a two way binding, that's done in XAML... within the binding directive, just press space bar and the property window will show binding modes. Pick two way.
PS: In Visual Studio, when you first create the DP just type in propdp and press the tab button, the entire DP structure is inserted for you automatically. As a result DPs are easy to implement.
How do DPs work two-way?
If you are using XAML binding you simply tell it via the MODE property that it's two way. This means that the GUI changes will update the properties when user changes the values.
<TextBox Text="{Binding Path=ThePropertyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Notice the Mode value and the UpdateSourceTrigger says, don't want for focus to change, update it right away.
Wait a minute, nothing is happening when I bind and make changes!
Three things are required for a binding to work 1) DataContext must be set either in code behind or as a static resource in the XAML 2) The pathname to the property name must exactly the CLR property name and 3) There must be content in the property.
Can I fire an Event from somewhere else to update the property?
Sure... the first step is to set up the static eventhandler in the UserControl like this:
public static EventHandler<string> NewData;
Then in the CTOR wire it up like this:
NewData+=OnNewData;
Then the event handler looks like this:
private void OnNewData(object sender, string data){
//setting this string property notifies WPF to update the GUI
ThePropertyName = data;
}
The other code does this...
MyUserControl.OnNewData(this, "Now is the time for all good men to...");
Without breaking MVVM, is there a way to expose some properties of a child control in a user control so that the window or other user control that utilizes it can access these properties directly?
For instance I have a user control that has a listview set up with gridviewcolumns, headers, and is bound to a view model. But the list view in the user control has selected item properties and such that I'd like to expose to the host without having to do something like usercontrol.customListView.property. Or is that how I should do it? I'd like to go just usercontrol.property, omitting customListView. Perhap I should just create properties in the user controls code behind that return the list view controls properties that I want attached directly to the user control?
I feel like that latter option doesn't really break MVVM since they are exposed for the host to interact with, not really related to the view itself. Any suggested would be appreciated.
EDIT: In fact, I'd really like to have a SelectedItem property directly on the user control that is not ListViewItem or object, but actually of the datatype contained that doe like:
public MyDataType SelectedItem {
get {
return customListView.SelectedItem as MyDataType;
}
}
Would that be permissible in MVVM? Because I don't see how I could have that in the ViewModel, seems like it would have to be in the partial class code behind.
This is pretty common task when you want to put something repeated into UserControl. The simplest approach to do so is when you are not creating specialized ViewModel for that UserControl, but sort of making custom control (build with the use of UserControl for simplicity). End result may looks like this
<UserControl x:Class="SomeNamespace.SomeUserControl" ...>
...
<TextBlock Text="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}" ...>
</UserControl>
.
public partial class SomeUserControl : UserControl
{
// simple dependency property to bind to
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(SomeUserControl), new PropertyMetadata());
// has some complicated logic
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(SomeUserControl),
new PropertyMetadata((d, a) => ((SomeUserControl)d).ValueChanged()));
private void ValueChanged()
{
... // do something complicated here
// e.g. create complicated dynamic animation
}
...
}
Usage will looks like this in containing window
<l:SomeUserControl Text="Text" Value="{Binding SomeValue}" ... />
As you can see SomeValue is bound to Value and there is no MVVM violations.
Of course, you can create a proper ViewModel if view logic is complicated or required too much bindings and it's rather easier to allow ViewModels to communicate directly (via properties/methods).
I have a MVVM Windows Phone 8 app. The XAML page has a user control that I created that needs to be notified when a change takes place in the View Model. To facilitate this, I created an int property in the user control to be bound to a property in the View Model, so the user control property's setter method would be triggered when the property it was bound to in the View Model changed.
Using the code below, the user control's VideosShownCount property does show up in the Property List at design-time but when I click on the binding mini-button, the Create Data Binding option is greyed out in the pop-up menu.
So I have one or two questions, depending on what is the root problem:
1) How do I make a property in a View Model available as a Data Binding source?
2) How do I format a user control property so the IDE allows it to be data bound to a View Model property?
private int _videosShownCount = 0;
public int VideosShownCount
{
get
{
return this._videosShownCount;
}
set
{
this._videosShownCount = value;
}
}
public static readonly DependencyProperty VideoShownCountProperty =
DependencyProperty.Register("VideosShownCount", typeof(int), typeof(MyUserControl),
new PropertyMetadata(0, new PropertyChangedCallback(VideoShownCountPropertyChanged)));
static void VideoShownCountPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
MyUserControl MyUserUserControl = (MyUserControl)sender;
// Don't care about the value, just want the notification.
// int val = (int)e.NewValue;
// Do work now that we've been notified of a change.
MyUserUserControl.DoWork();
}
You're not using the DependencyProperty for your property, which will definitely cause problems between your code and the bindings
public int VideosShownCount
{
get { return (int) GetValue(VideosShownCountProperty); }
set { SetValue(VideosShownCountProperty, value); }
}
I'm not sure if this is the main cause of your problem, but it's worth fixing regardless.