1
votes

I’m new to .Net and C# and a little bit confused with bindings. I have application that implements MVVM and displays DataGrid. What I want to implement is when user presses certain key combination then the content of currently selected cell gets copied to the cell in the row below. I have tried binding the SelectedItem of DataGrid to the ViewModel property but it never gets updated. CommandParameter also didn’t work, item count is always 0. So I cannot extract what cell the user has selected and cannot read the content of the selected cell. Does anyone have suggestions how to solve this problem or implement this functionality? Thanks in advance. code: xaml:

<DataGrid Grid.Row="1" 
          Grid.Column="0" 
          Grid.ColumnSpan="9" 
          AutoGenerateColumns="False" 
          Height="Auto" 
          HorizontalAlignment="Left" 
          Name="dataGrid" 
          VerticalAlignment="Top" 
          Width="{Binding ElementName=grid4,Path=Width}"
          ScrollViewer.CanContentScroll="False" 
          FrozenColumnCount="1" 
          SelectionUnit="Cell" 
          SelectionMode="Extended" 
          CanUserSortColumns = "False" 
          CanUserReorderColumns="False" 
          CanUserResizeRows="False" 
          RowHeight="25" 
          RowBackground="LightGray" 
          AlternatingRowBackground="White"
          ScrollViewer.VerticalScrollBarVisibility="Visible" 
          ScrollViewer.HorizontalScrollBarVisibility="Visible"
          ItemsSource="{Binding Layers, Mode=TwoWay}"  
          SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Selection, Mode=TwoWay}">
    <DataGrid.InputBindings>
        <KeyBinding Gesture="Shift" 
                    Command="{Binding ItemHandler}" 
                    CommandParameter="{Binding ElementName=dataGrid, Path=SelectedItems}">
        </KeyBinding>
    </DataGrid.InputBindings>
</DataGrid>

ViewModel:

private float _selection = 0.0f;
    public float Selection
    {
        get
        {
            return _selection;
        }
        set
        {
            if (_selection != value)
            {
                _selection = value;
                NotifyPropertyChanged("Selection");
            }
        }
    }

...

        public DelegateCommand<IList> SelectionChangedCommand = new DelegateCommand<IList>(
        items =>
        {
            if (items == null)
            {
                NumberOfItemsSelected = 0; 
                return;
            }
            NumberOfItemsSelected = items.Count;
        });
    public ICommand ItemHandler
    {
        get
        {
            return SelectionChangedCommand;
        }
    }
2
is Layers a collection of float elements?blindmeis
Layers is a ObservableCollection that contains Layer objects. Class Layer implements INotifyPropertyChanged and has float fields. DataGrid cell displays one Layer float field.Bogdan
maybe you should add what you wanna achieve. i'm right with: when a user click a cell the the next row same cell should be the same value? strange requirement. what happens when user click a cell in the last row?blindmeis
"when a user click a cell the the next row same cell should be the same value" yep, that's the requirement. (Though click is rather a shortcut like "Ctrl+D") If a cell is in the last row then nothing happens.Bogdan

2 Answers

8
votes

EDIT: SelectionUnit=Cell dont work with SelectedItem

thats the normal mvvm way:

1st the object type you wanna display in each row

 public class MyObject {}

2nd a viewmodel thats hold the collection and the selected item

 public class MyViewmodel
 {
    public ObservableCollection<MyObject> MyItems {get;set;}
    public MyObject MySelectedItem {get;set;}

 }

xaml datagrid

 <DataGrid ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem, Mode=TwoWay}"/>

so thats what you have. if you now wanna create a command for "copy" a row you could create a new object MyObject and copy the values from your MySelectedItem and then add the new MyObject to your collection.

but maybe i dont get your question right.

0
votes

I think you can add a depency propery. this is the property answer:

public class UIElementMouseRightButtonDownCommandBehavior : CommandBehaviorBase<UIElement>
{
    public UIElementMouseRightButtonDownCommandBehavior(UIElement obj)
        : base(obj)
    {
        if (obj == null) throw new System.ArgumentNullException("obj");
        obj.MouseRightButtonDown += OnMouseRightButtonDown;
    }

    private void OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
        ExecuteCommand();
    }       
}

And another class:

 public class MouseRightButtonDown
{
    private static readonly DependencyProperty MouseRightButtonDownCommandBehaviorProperty = DependencyProperty.RegisterAttached(
        "MouseRightButtonDownCommandBehavior",
        typeof(UIElementMouseRightButtonDownCommandBehavior),
        typeof(MouseRightButtonDown),
        null);

    public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
        "Command",
        typeof(ICommand),
        typeof(MouseRightButtonDown),
        new PropertyMetadata(OnSetCommandCallback));

    public static void SetCommand(UIElement element, ICommand command)
    {
        element.SetValue(CommandProperty, command);
    }

    public static ICommand GetCommand(UIElement element)
    {
        return element.GetValue(CommandProperty) as ICommand;
    }

    private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        UIElement element = dependencyObject as UIElement;
        if (element != null)
        {
            UIElementMouseRightButtonDownCommandBehavior behavior = GetOrCreateBehavior(element);
            behavior.Command = e.NewValue as ICommand;
        }
    }

    private static UIElementMouseRightButtonDownCommandBehavior GetOrCreateBehavior(UIElement element)
    {
        UIElementMouseRightButtonDownCommandBehavior behavior = element.GetValue(MouseRightButtonDownCommandBehaviorProperty) as UIElementMouseRightButtonDownCommandBehavior;
        if (behavior == null)
        {
            behavior = new UIElementMouseRightButtonDownCommandBehavior(element);
            element.SetValue(MouseRightButtonDownCommandBehaviorProperty, behavior);
        }
        return behavior;
    }
}

then in your view model file:

public ICommand SelectNameCommand
    {
        get { return new Command(P => SelectName()); }
    }

I think your XAML is correct.