UserControl with custom ItemsSource doesn't detect changes - c#

I have simple UserControl where is defined property ItemsSource
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(Dictionary<string, object>), typeof(UserControl1), new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(UserControl1.OnItemsSourceChanged)));
public Dictionary<string, object> ItemsSource
{
get { return (Dictionary<string, object>)GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
}
}
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl1 control = (UserControl1)d;
control.DisplayInControl();
}
I want to make this property update dynamically, but I am wondered why OnItemsSourceChanged doesn't fired every time when something happend with ItemsSource. So I am upset.
I've tried Custom ItemsSource property for a UserControl but this doesn't help or I've written bad newValueINotifyCollectionChanged_CollectionChanged function
My control is from this post CodeProject
My Code :
UserControl XAML - http://pastie.org/10606317
UserControl CodeBehind - http://pastie.org/10606322
Control Usage -
<controls:MultiSelectComboBox SelectedItems="{Binding SelectedCategories, Mode=TwoWay}" Grid.Column="0" Grid.Row="0" x:Name="CategoriesFilter" DefaultText="Category" ItemsSource="{Binding Categories }" Style="{StaticResource FiltersDropDowns}"/>
Update : I've made small step to solution. I have next style :
<Style.Triggers>
<DataTrigger Binding="{Binding ItemsSource, RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding ItemsSource.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
which I apply to my control (make control disabled if no itemSource). As I update control source on click, I see that control becomes enabled, so ItemsSource aren't empty (from start it is). So problem now is just in Redrawing control content if I understand this behaviour correctly.

If you have a dictionary as your data type, and you add or remove a value from the dictionary, then your property did not actually change. This event will only fire if you have actually set this property to reference a different dictionary.
If you need wpf to automatically detect if an item is added or removed from the dictionary, you should use an ObservableCollection.

Replace Dictionary with ObservableCollection, Dictionary won't fire the propertyChanged event when add, update, delete an item.
Write your own Dictionary to fire the propertychanged event manually.

I see that the problem is that you are building a new ItemsSource for the ComboBox control within your custom UserControl, and that you are expecting that MultiSelectCombo.ItemsSource to stay synced with your UserControl1.ItemsSource. This can't happen when simply binding a Dictionary, and it won't happen even when binding an ObservableCollection -- unless you explicitly handle it.
To accomplish what you are after, first you will need ItemsSource of your custom control to be of a type that does notify us of collection changes, such as the ObservableCollection (which I'll use for this example, as you've done in your links above). Then, you'll need to update the ItemsSource of your ComboBox within your control, to keep them in sync.
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (MultiSelectComboBox)d;
// Since we have to listen for changes to keep items synced, first check if there is an active listener to clean up.
if (e.OldValue != null)
{
((ObservableCollection<Node>)e.OldValue).CollectionChanged -= OnItemsSource_CollectionChanged;
}
// Now initialize with our new source.
if (e.NewValue != null)
{
((ObservableCollection<Node>)e.NewValue).CollectionChanged += OnItemsSource_CollectionChanged;
control.DisplayInControl();
}
}
private void OnItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
DisplayInControl();
}
Now, that said, the solution above is for a more generic case, where you might need to do something with the ItemsSource given to your custom control before passing it onto your MultiSelectCombo.ItemsSource. In your current case, however, you are simply building a collection of Node to exactly match the given collection of Node. If that's guaranteed to be the case, your solution can be much, much simpler:
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (MultiSelectComboBox)d;
control.MultiSelectCombo.ItemsSource = (IEnumerable)e.NewValue;
}
Just let the wrapped ComboBox use the ItemsSource given to your custom control.

Related

Access DataGrid.RowStyle in AttachedProperty

XAML:
<DataGrid ItemsSource="{Binding Source={StaticResource Lines}}"
uiwpf:DataGridExtensions.CanExportToExcel="True">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ContextMenu" Value="{StaticResource RowContextMenu}" />
</Style>
</DataGrid.RowStyle>
...
</DataGrid>
AttachedProperty:
private static void CanExportToExcelChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
//Just my way of secure casting DependencyObject -> DataGrid
if(d is DataGrid dataGrid)
{
Debug.Assert(dataGrid.RowStyle != null, "Why is this null?");
}
}
Problem: Assert is getting triggered - WHY ?
This is probably the order in which the properties are being set on the DataGrid.
In general (I don't know of any exceptions, but I don't want to claim there aren't any) properties are set in the order that they're defined in XAML. So your DataGridExtensions.CanExportToExcel will be set to True before DataGrid.RowStyle is set.
You can test this by removing your current call to uiwpf:DataGridExtensions.CanExportToExcel="True", and putting:
<uiwpf:DataGridExtensions.CanExportToExcel>True</uiwpf:DataGridExtensions.CanExportToExcel>
After you set <DataGrid.RowStyle>.
To make your attached property robust, you will probably need to use CanExportToExcelChanged to set a binding on the RowStyle property (and remove it again when CanExportToExcel is set to False).

WPF: TwoWay binding is ALWAYS updated - OneWay binding is ONLY ONCE updated

I know what you think: It is 2017, please don't come up with this again, but I really can not find any valueable explanation for this.
Please have a look at the ActiveNotes property in this XAML-Code.
I have this TwoWay binding in my XAML, which works perfectly. It is ALWAYS updated, if the PropertyChanged event for ScaleNotes is fired and if the binding is set to TwoWay.
<c:Keyboard
Grid.Row="2"
Grid.Column="0"
PlayCommand="{Binding PlayCommand}"
StopCommand="{Binding StopCommand}"
ActiveNotes="{Binding ScaleNotes, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The ScaleNotes property in the ViewModel looks like this. Whenever it changes, the PropertyChanged event is guaranteed to be fired. I checked and double checked it. The business logic in the ViewModel works.
private ReadOnlyCollection<eNote> _ScaleNotes;
public ReadOnlyCollection<eNote> ScaleNotes
{
get { return _ScaleNotes; }
set { SetField(ref _ScaleNotes, value); }
}
[DebuggerStepThrough]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
[DebuggerStepThrough]
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
Up to here everything is ok. Whenever the ScaleNotes property in the VM is changed, the target property ActiveNotes is updated.
Now the problem:
If I only change the binding in the XAML to OneWay and the business logic in the VM stays 100% the same, the ActivesNotes property in the target object is updated only once even if the PropertyChanged event is fired. I checked and double checked it. The PropertyChanged event for the ScaleNotes property is always fired.
<c:Keyboard
Grid.Row="2"
Grid.Column="0"
PlayCommand="{Binding PlayCommand}"
StopCommand="{Binding StopCommand}"
ActiveNotes="{Binding ScaleNotes, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
Just to make this complete, here is DP in the target object.
public static DependencyProperty ActiveNotesProperty = DependencyProperty.Register(
"ActiveNotes",
typeof(ReadOnlyCollection<eNote>),
typeof(Keyboard),
new PropertyMetadata(OnActiveNotesChanged));
public ReadOnlyCollection<eNote> ActiveNotes
{
get
{
return (ReadOnlyCollection<eNote>)GetValue(ActiveNotesProperty);
}
set
{
SetValue(ActiveNotesProperty, value);
}
}
private static void OnActiveNotesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Keyboard keyboard = (Keyboard)d;
keyboard.ActiveNotes = (ReadOnlyCollection<eNote>)e.NewValue;
if ((keyboard.ActiveNotes != null) && (keyboard.ActiveNotes.Count > 0))
{
keyboard.AllKeys.ForEach(k => { if ( k.Note != eNote.Undefined) k.IsActiveKey = true; });
keyboard.AllKeys.ForEach(k => { if ((k.Note != eNote.Undefined) && (!keyboard.ActiveNotes.Contains(k.Note))) k.IsActiveKey = false; });
}
else
{
keyboard.AllKeys.ForEach(k => { if (k.Note != eNote.Undefined) k.IsActiveKey = true; });
}
}
I don't understand this. From my knowledge, OneWay and TwoWay only define in which direction the values are updated and not how often they can be updated.
I can not understand, that everything works fine with TwoWay, the business logic stays 100% the same and OneWay is a deal breaker.
If you ask yourself, why I want to know this: This binding was planned as a OneWay binding. It makes no sense to update the source in any way. I only changed it to TwoWay, because OneWay doesn't work as expected.
SOLUTION with the help of #MikeStrobel: (see comments)
The code needs be changed this way:
private static void OnActiveNotesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Keyboard keyboard = (Keyboard)d;
//THIS LINE BREAKED THE CODE, WHEN USING OneWay binding BUT NOT WITH TwoWay binding
//keyboard.ActiveNotes = (ReadOnlyCollection<eNote>)e.NewValue;
if ((keyboard.ActiveNotes != null) && (keyboard.ActiveNotes.Count > 0))
{
keyboard.AllKeys.ForEach(k => { if ( k.Note != eNote.Undefined) k.IsActiveKey = true; });
keyboard.AllKeys.ForEach(k => { if ((k.Note != eNote.Undefined) && (!keyboard.ActiveNotes.Contains(k.Note))) k.IsActiveKey = false; });
}
else
{
keyboard.AllKeys.ForEach(k => { if (k.Note != eNote.Undefined) k.IsActiveKey = true; });
}
}
Using a OneWay binding, the assignment in the OnActiveNotesChanged event handler method deletes or clears out the binding. Mike is correct saying, that the assingment is completely unnecessary, because at this point of time the value is already set before in the property. So it makes no sense at all, no matter if I use OneWay or TwoWay binding.
Dependency properties have a complex system of precedence. The value of a dependency property at any given time may come from various sources: bindings, style setters, trigger setters, etc. Local values have the highest priority, and when you set a local value, you suppress values coming from other sources.
In the case of a binding, setting a local value will cause a source-to-target binding (OneWay or OneTime) to be *removed*. However, when you set a local value on a property with a target-to-source binding (TwoWay or OneWayToSource), the binding will be maintained, and the local value you assigned will get propagated back to the source.
In your case, the issue is here:
keyboard.ActiveNotes = (ReadOnlyCollection<eNote>)e.NewValue;
In your OnActiveNotesChanged handler, you're assigning a new local value to ActiveNotes, which is causing your OneWay binding to be removed. Fortunately, the solution is simple: you can remove this line entirely, as it is redundant. In most cases it is unnecessary to assign a dependency property in its own change handler—the new value has already been applied. (And if you want an opportunity to replace a proposed value before it gets applied, the place to do that would be a CoerceValueCallback, which you can also specify in you PropertyMetadata.)
Let me share my experience after spending sleepless nights on this issue!
I stumbled into this issue on a slightly different use case for which I could not find a solution, but thanks to the advice above I could finally solve it.
To put it short, I faced the same Binding dissociation issue but without having code behind accessing the incriminated property at all! The issue occurred on a CustomControl!
In fact, you must also be aware that if a CustomControl declares a DependencyProperty which is used in a Binding both by the consumer of the CustomControl and inside the Template of the CustomControl, then you MUST ensure that our Binding in the Template of the CustomControl is eighter of type TemplateBinding or a regular binding using OneWay or OneWayToSource.
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof(bool), typeof(QAToggle), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None));
public bool IsChecked
{
get => (bool)this.GetValue(IsCheckedProperty);
set => this.SetValue(IsCheckedProperty, value);
}
If not, the Binding used in the UserControl's template when updating will also set a new value to the CustomControls Property thus removing the external binding set up by the consumer of the CustomControl.
<Style TargetType="{x:Type ccont:QAToggle}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="MinHeight" Value="23" />
<Setter Property="MinWidth" Value="75" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ccont:QAToggle}">
<ToggleButton x:Name = "QAToggle"
Command = "{TemplateBinding Command}"
CommandParameter = "{TemplateBinding CommandParameter}"
FontSize = "{TemplateBinding FontSize}"
FontWeight = "{TemplateBinding FontWeight}"
FontFamily = "{TemplateBinding FontFamily}"
IsThreeState = "False"
IsChecked = "{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ccont:QAToggle}}">
Here the line IsChecked = "{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ccont:QAToggle}}" will break the external binding. To avoid this change it to:
IsChecked = "{TemplateBinding IsChecked}"
or
IsChecked = "{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ccont:QAToggle}, Mode=OneWay}"

WPF - context menu over DataGrid

My DataGrid is bound to a data source, which is a database. When user right clicks somewhere over the DataGrid control, I'd like to be able to recognize over which column he or she did it. Scenario is as follows - if ContextMenu is opened over a column holding dates, then (for example) I would like to present him with options to filter out dates smaller, greater or equal to the date selected.
<DataGrid.Resources>
<Helpers:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
<DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding Path=DataContext}">
<MenuItem Header="Cokolwiek" Command="{Binding Source={StaticResource proxy}, Path=Data.FK}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent.PlacementTarget.DataContext}"/>
</ContextMenu>
</DataGrid.ContextMenu>
PlacementTarget is a reference to the DataGrid, and I'd like it to be a reference to DataGridColumn.
BindingProxy class:
public class BindingProxy : Freezable {
protected override Freezable CreateInstanceCore() {
return new BindingProxy();
}
public object Data {
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy), new UIPropertyMetadata(null));
}
What you can do is hook up to the PreviewMouseUp event so you can look at the Source property of the Event that is raised.
With the exception of direct events, WPF defines most routed events in pairs - one tunnelling and the other bubbling. The tunnelling event name always begins with 'Preview' and is raised first. This gives parents the chance to see the event before it reaches the child. This is followed by the bubbling counterpart. In most cases, you will handle only the bubbling one. The Preview would be usually used to
block the event (e.Handled = true) cause the parent to do something in
advance to normal event handling.
e.g. if UI Tree = Button contains Grid contains Canvas contains Ellipse
Clicking on the ellipse would result in (MouseDownButton is eaten up by Button and Click is raised instead.)
private void OnPreviewMouseUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
var source = mouseButtonEventArgs.Source;
// Assuming the DataGridColumn's Template is just a TextBlock
// but depending on the Template which for sure should at least inherit from FrameworkElement to have the Parent property.
var textBlock = source as TextBlock;
// Not a good check to know if it is a holding dates but it should give you the idea on what to do
if (textBlock != null)
{
var dataGridColumn = textBlock.Parent as DataGridColumn;
if (dataGridColumn != null)
{
if ((string) dataGridColumn.Header == "Holding Dates")
{
// Show context menu for holding dates
}
}
}
// Other stuff
else if (somethingElse)
{
// Show context menu for other stuff
}
}

Cant set Frame.Source via triggers after NavigationCommands.GoToPage is used

I am trying to create a MVVM-ready custom control that extends Frame (from Navigation), with two primary goals in mind.
Firstly, I want to be able to change the Source of the frame via triggers. This way, I can change which view is used based for some cases when a property of the ViewModel changes.
Secondly, I want the views themselves to be able to change which view is used, for certain cases where nothing in the ViewModel changes. Using a Frame/Page system and calling the NavigationCommands.GoToPage Command from inside the pages seemed to be the most appropriate way to do this, since each different view can be defined as a page.
The problem I'm running into is that setting Frame.Source via triggers works perfectly fine until the first time GoToPage is used. After that, the triggers seem to have no effect. GoToPage appears to work all of the time, any time. I've been searching all day and can't find any documentation that explains this.
Anyhow, here's the implementation for my custom Frame, where all I do is bind to GoToPage and ensure Pages inherit DataContext:
public class FrameExtended : Frame
{
public FrameExtended()
{
CommandBindings.Add(new CommandBinding(NavigationCommands.GoToPage, GoToPage_Executed));
Navigated += new System.Windows.Navigation.NavigatedEventHandler(FrameExtended_Navigated);
}
void FrameExtended_Navigated(object sender, NavigationEventArgs e)
{
(Content as FrameworkElement).DataContext = this.DataContext;
}
void GoToPage_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (e.Parameter is Uri) Source = e.Parameter as Uri;
else if (e.Parameter is string) Source = new Uri(e.Parameter as string, UriKind.Relative);
}
}
Here's a test case for my ViewModel, which is about as simple as a ViewModel gets:
public enum MyEnum { MyEnumVal1, MyEnumVal2, MyEnumVal3 }
public class ViewModel : INotifyPropertyChanged
{
private MyEnum enumVal = MyEnum.MyEnumVal1;
public MyEnum EnumVal
{
get { return enumVal; }
set
{
if (enumVal != value)
{
enumVal = value;
OnPropertyChanged("EnumVal");
}
}
}
protected void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
And here's test snippet of XAML where I employ the frame and define a few triggers:
<Button Content="ChangeViewModel" Click="Button_Click"/>
<Control>
<Control.Template>
<ControlTemplate TargetType="Control">
<local:FrameExtended x:Name="MyFrame"/>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding EnumVal}" Value="MyEnumVal1">
<Setter TargetName="MyFrame" Property="Source" Value="Pages/testpage.xaml"/>
</DataTrigger>
<DataTrigger Binding="{Binding EnumVal}" Value="MyEnumVal2">
<Setter TargetName="MyFrame" Property="Source" Value="Pages/testpage2.xaml"/>
</DataTrigger>
<DataTrigger Binding="{Binding EnumVal}" Value="MyEnumVal3">
<Setter TargetName="MyFrame" Property="Source" Value="Pages/testpage3.xaml"/>
<Setter TargetName="MyFrame" Property="Background" Value="Green"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Control.Template>
</Control>
Where Button_Click refers to:
private void Button_Click(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as ViewModel;
switch (vm.EnumVal)
{
case MyEnum.MyEnumVal1: vm.EnumVal = MyEnum.MyEnumVal2; break;
case MyEnum.MyEnumVal2: vm.EnumVal = MyEnum.MyEnumVal3; break;
case MyEnum.MyEnumVal3: vm.EnumVal = MyEnum.MyEnumVal1; break;
}
}
Aside from that, testpage.xaml contains the following line:
<Button Content="NEXTPAGE" Command="GoToPage" CommandParameter="Pages/testpage2.xaml"/>
Where the rest of the pages just have a TextBlock indicating which page it is.
If I click the "ChangeViewModel" button over and over again, the frame will cycle through the pages as expected (and changing its background to green when it's on page 3). Once I click the button inside testpage.xaml that calls GoToPage, the Frame switches to testpage2. After that, subsequent clicks on ChangeViewModel never change which page is displayed, but still make the Frame's background green every 3rd click (when testpage3 should show).
The workaround I've found for this issue is to use Frame.Navigate() in my GoToPage handler defined in the custom control, instead of setting the source property directly. It seems to be that setting the Source property from C# code is what prevents triggers from changing it (different priority levels, I guess). Navigate() doesn't have this issue, and it's actually what I tried first and is most likely the ideal implementation. It didn't work before because all of these implementations were in a Control Library and Navigate() was searching for my pages in the executing assembly instead. As a side note for anyone else trying to do something similar, be sure to include the assembly reference in any URI you pass to Frame.Navigate(), as it will otherwise search in the executing assembly by default (not necessarily the defining assembly, as with URIs specified in XAML). My GoToPage handler now looks like this:
void GoToPage_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (e.Parameter is Uri) Navigate(e.Parameter as Uri);
else if (e.Parameter is string) Navigate(new Uri(e.Parameter as string, UriKind.RelativeOrAbsolute));
}

Using ICommand and InputBindings consistently in DataGrid

I'm trying to create a DataGrid having the following features:
Readonly Datagrid, but provide editing capabilities through double click and separate edit form (double click on specific row)
ContextMenu which calls new/edit/delete form (right click on whole DataGrid)
Delete key which calls delete form (on specific selected row)
I thought it would be a good idea to use ICommand, so I created a DataGrid like this:
public class MyDataGrid : DataGrid {
public static readonly RoutedCommand NewEntry = new RoutedCommand();
public static readonly RoutedCommand EditEntry = new RoutedCommand();
public static readonly RoutedCommand DeleteEntry = new RoutedCommand();
public MyDataGrid() {
CommandBindings.Add(new CommandBinding(NewEntry, ..., ...));
CommandBindings.Add(new CommandBinding(EditEntry, ..., ...));
CommandBindings.Add(new CommandBinding(DeleteEntry, ..., ...));
InputBindings.Add(new InputBinding(DeleteCommand, new KeyGesture(Key.Delete)));
InputBindings.Add(new MouseBinding(EditEntry, new MouseGesture(MouseAction.LeftDoubleClick)));
// ContextMenu..working fine
}
}
I then realized, double-clicking a row isn't working, so I added this:
LoadingRow += (s, e) =>
e.Row.InputBindings.Add(new MouseBinding(EditEntry,
new MouseGesture(MouseAction.LeftDoubleClick)));
And of course the delete key isn't working either, I added this:
PreviewKeyDown += (s, e) => { if(e.Key == Key.Delete) { ... } };
Why do I have to do that? Isn't the whole point of having Commands to prevent this kind of hacking around with events? Do I miss something?
In my simple and perfect world, I want to decide in the CanExecute method whether it is appropriate to handle the command, and not subscribe to tons of different event handlers..
Usually I attach the command to the DataGridCell using a Style
Here's an example using a custom AttachedCommandBehavior
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="my:CommandBehavior.Command" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MyView}}, Path=DataContext.ShowPopupCommand}" />
<Setter Property="my:CommandBehavior.CommandParameter" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}, Path=DataContext}" />
<Setter Property="my:CommandBehavior.Event" Value="MouseDoubleClick" />
</Style>
I can't remember why I attached it to the Cell instead of the Row, but I'm sure there was a reason. You could try attaching the event to the Row instead and see what happens.

Categories

Resources