Редактиране на новодобавен ред в Silverlight 3 DataGrid с помощта на MVVM

Опитвам се да използвам Silverlight 3.0 DataGrid с шаблона за проектиране MVVM. Моята страница има DataGrid и бутон, който добавя елемент към колекцията във VM с помощта на команда (от библиотеката на композитните приложения). Това работи добре и новият елемент се показва и избира.

Проблемът, който не мога да разреша, е как да започна да редактирам реда. Искам новият ред да може да се редактира веднага, когато потребителят щракне върху бутона Добавяне, т.е. фокусът е зададен на DataGrid и новият ред е в режим на редактиране.

Това е XAML в изгледа:

<Grid x:Name="LayoutRoot">
    <StackPanel>
        <data:DataGrid ItemsSource="{Binding DataView}"/>
        <Button cmd:Click.Command="{Binding AddItemCommand}" Content="Add" />
    </StackPanel>
</Grid>

Кодът отзад има един ред код, който създава екземпляр на VM и задава DataContext на изгледа.

VM кодът е:

public class VM 
{
    public List<TestData> UnderlyingData { get; set; }
    public PagedCollectionView DataView { get; set; }
    public ICommand AddItemCommand { get; set; }

    public VM()
    {
        AddItemCommand = new DelegateCommand<object>(o =>
            {
                DataView.AddNew();
            });

        UnderlyingData = new List<TestData>();
        UnderlyingData.Add(new TestData() { Value = "Test" });

        DataView = new PagedCollectionView(UnderlyingData);
    }
}

public class TestData
{
    public string Value { get; set; }

    public TestData()
    {
        Value = "<new>";
    }

    public override string ToString()
    {
        return Value.ToString();
    }
}

Какъв би бил най-добрият начин за решаване на този проблем с помощта на шаблона за проектиране на MVVM?


person bart    schedule 17.12.2009    source източник


Отговори (2)


Сблъсках се със същия проблем. Въведох интерфейс ISupportEditingState:

public interface ISupportEditingState
{
    EditingState EditingState { get; set; }
}

Моята VM го прилага. И тогава написах това поведение, за да синхронизирам състоянието на редактиране на DataGrid и моята VM:

    public class SynchroniseDataGridEditingStateBehaviour : Behavior<DataGrid>
{
    public static readonly DependencyProperty EditingStateBindingProperty =
        DependencyProperty.Register("EditingStateBinding", typeof(ISupportEditingState),
        typeof(SynchroniseDataGridEditingStateBehaviour), new PropertyMetadata(OnEditingStateBindingPropertyChange));

    private bool _attached;
    private bool _changingEditingState;

    public ISupportEditingState EditingStateBinding
    {
        get { return (ISupportEditingState)GetValue(EditingStateBindingProperty); }
        set { SetValue(EditingStateBindingProperty, value); }
    }

    private static void OnEditingStateBindingPropertyChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var b = d as SynchroniseDataGridEditingStateBehaviour;
        if (b == null)
            return;

        var oldNotifyChanged = e.OldValue as INotifyPropertyChanged;
        if (oldNotifyChanged != null)
            oldNotifyChanged.PropertyChanged -= b.OnEditingStatePropertyChanged;

        var newNotifyChanged = e.NewValue as INotifyPropertyChanged;
        if (newNotifyChanged != null)
            newNotifyChanged.PropertyChanged += b.OnEditingStatePropertyChanged;

        var newEditingStateSource = e.NewValue as ISupportEditingState;
        if (newEditingStateSource.EditingState == EditingState.Editing)
        {
            // todo: mh: decide on this behaviour once again.
            // maybe it's better to start editing if selected item is already bound in the DataGrid
            newEditingStateSource.EditingState = EditingState.LastCancelled;
        }
    }

    private static readonly string EditingStatePropertyName = 
        CodeUtils.GetPropertyNameByLambda<ISupportEditingState>(ses => ses.EditingState);

    private void OnEditingStatePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (_changingEditingState || !_attached || e.PropertyName != EditingStatePropertyName)
            return;

        _changingEditingState = true;

        var editingStateSource = sender as ISupportEditingState;
        if (editingStateSource == null)
            return;

        var grid = AssociatedObject;
        var editingState = editingStateSource.EditingState;
        switch (editingState)
        {
            case EditingState.Editing:
                grid.BeginEdit();
                break;
            case EditingState.LastCancelled:
                grid.CancelEdit();
                break;
            case EditingState.LastCommitted:
                grid.CommitEdit();
                break;
            default:
                throw new InvalidOperationException("Provided EditingState is not supported by the behaviour.");
        }

        _changingEditingState = false;
    }

    protected override void OnAttached()
    {
        var grid = AssociatedObject;
        grid.BeginningEdit += OnBeginningEdit;
        grid.RowEditEnded += OnEditEnded;
        _attached = true;
    }

    protected override void OnDetaching()
    {
        var grid = AssociatedObject;
        grid.BeginningEdit -= OnBeginningEdit;
        grid.RowEditEnded -= OnEditEnded;
        _attached = false;
    }

    void OnEditEnded(object sender, DataGridRowEditEndedEventArgs e)
    {
        if (_changingEditingState)
            return;

        EditingState editingState;
        if (e.EditAction == DataGridEditAction.Commit)
            editingState = EditingState.LastCommitted;
        else if (e.EditAction == DataGridEditAction.Cancel)
            editingState = EditingState.LastCancelled;
        else
            return; // if DataGridEditAction will ever be extended, this part must be changed
        EditingStateBinding.EditingState = editingState;
    }

    void OnBeginningEdit(object sender, DataGridBeginningEditEventArgs e)
    {
        if (_changingEditingState)
            return;
        EditingStateBinding.EditingState = EditingState.Editing;
    }
}

Работи добре за мен, надявам се да помогне.

person nayato    schedule 20.08.2010
comment
Благодаря за публикуването на кода. Не мога лесно да го тествам, защото вече не работя по този проект, но ако някой друг го намери за полезен, може би може да коментира тук. - person bart; 24.08.2010

Всеки път, когато говорите за директен достъп до компоненти на потребителския интерфейс, пропускате смисъла на mvvm. Интерфейсът се свързва с модела на изглед, така че намерете начин да промените модела на изглед вместо това.

person Pierreten    schedule 17.12.2009
comment
Точно. Бих могъл да разреша това лесно, ако не използвах MVVM. Въпросът ми е как да постигна резултата, който искам, докато все още се придържам към модела MVVM. - person bart; 17.12.2009