C # Как отменить изменения в диалоге с шаблоном MVVM - PullRequest
0 голосов
/ 22 декабря 2018

Прежде всего, я - Java-разработчик, в настоящее время помогаю с проектом на C #, так что помните об этом, отвечая.

У меня следующий сценарий.У меня есть таблица данных с записями, и когда я дважды щелкаю, я открываю новое окно (диалог) с некоторыми полями и кнопкой сохранения и отмены.Я хочу, чтобы кнопка отмены отменила все изменения, сделанные в диалоговом окне.

Я бы сказал, что это очень распространенный сценарий, но мне нужно найти один единственный ответ на stackoverflow, который действительно работает и не требуетГазиллион строк стандартного кода.Я не собираюсь вставлять сюда весь свой проект и постараюсь сделать доступным соответствующий код

Galaxy и SelectedJob - это простые JSON-объекты, которые сериализуемы и не имеют ничего, кроме методов получения и установки.пример.Job.cs

using System;
using System.Collections.Generic;

namespace GalaxyCreator.Model.Json
{
    [Serializable]
    public class Job
    {
        public String Id { get; set; }
        public String Name { get; set; }
        public Boolean StartActive { get; set; }
        public Boolean Disabled { get; set; }
        public Boolean Rebuild { get; set; }
        public Boolean Commandeerable { get; set; }
        public Boolean Subordinate { get; set; }
        public bool Buildatshipyard { get; set; } = true;
        public JobLocation JobLocation { get; set; }
        public JobCategory JobCategory { get; set; }
        public JobQuota JobQuota { get; set; }
        public IList<JobOrder> Orders { get; set; }
        public String Basket { get; set; }
        public String Encounters { get; set; }
        public String Time { get; set; }
        public Ship Ship { get; set; }
        public IList<String> Subordinates { get; set; }
    }
}

моя модель представления формы сетки данных.

using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaxyCreator.Dialogs.DialogFacade;
using GalaxyCreator.Model.Json;

namespace GalaxyCreator.ViewModel
{
    class JobEditorViewModel : ViewModelBase
    {
        private IDialogFacade dialogFacade = null;
        private RelayCommand<object> _jobEditorDetailClickedCommand;

        public Galaxy Galaxy { get; set; }
        public Job SelectedJob { get; set; }

        public JobEditorViewModel(Galaxy Galaxy)
        {
            this.Galaxy = Galaxy;
            this.dialogFacade = new DialogFacade();
        }

        public RelayCommand<object> JobEditorClickedCommand
        {
            get
            {
                if (_jobEditorDetailClickedCommand == null)
                {
                    _jobEditorDetailClickedCommand = new RelayCommand<object>((param) => JobEditorClicked(param));
                }

                return _jobEditorDetailClickedCommand;
            }
        }

        private void JobEditorClicked(object param)
        {
            Dialogs.DialogService.DialogResult result = this.dialogFacade.ShowJobEditorDetail("Job Editor Detail", param as Window, this.SelectedJob);           
        }
    }
}

Представление для этой модели представления

<UserControl x:Class="GalaxyCreator.View.JobEditorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:GalaxyCreator.View"
             xmlns:model="clr-namespace:GalaxyCreator.Model.JobEditor"
             xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
             xmlns:cmd ="http://www.galasoft.ch/mvvmlight"            
             mc:Ignorable="d" 
             d:DesignHeight="1000" d:DesignWidth="1500">
    <Grid>
        <Border BorderBrush="Black" BorderThickness="1" Padding="5">
            <StackPanel Orientation="Vertical" HorizontalAlignment="Center" Width="Auto">
                <Label Content="Job Editor" FontSize="14" HorizontalContentAlignment="Center" Width="Auto"/>
                <DataGrid x:Name="JobDataGrid" 
                          ItemsSource="{Binding Path=Galaxy.Jobs}" 
                          SelectedItem="{Binding Path=SelectedJob, Mode=TwoWay}" 
                    AutoGenerateColumns="False" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" 
                    ScrollViewer.HorizontalScrollBarVisibility="Auto" Height="500">
                    <DataGrid.Columns>
                        <DataGridTextColumn x:Name="JobId" Binding="{Binding Id}" Header="Id" IsReadOnly="True" />
                        <DataGridTextColumn x:Name="JobName" Binding="{Binding Name}" Header="Name" IsReadOnly="True" />
                    </DataGrid.Columns>
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="MouseDoubleClick">
                            <cmd:EventToCommand Command="{Binding Path=JobEditorClickedCommand, Mode=OneWay}"/>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </DataGrid>
            </StackPanel>
        </Border>
    </Grid>
</UserControl>

Ниже приведено несколько классов, которые откроют диалог и сделают несколько шаблонов, чтобы получить ответ из диалога и т. Д. ОниНе важно, я надеюсь на этот вопрос, поэтому я опускаю их и перехожу прямо к viewmodel диалоговой формы.Я читал кучу о шаблоне сувенира и держал где-то глубокий клон оригинального объекта и после отмены положил его обратно.Это звучало для меня как самое чистое решение из доступных.Кажется, есть еще дюжина таких явных, как updatemode или что-то в этом роде ... и затем обычно следуют 10000 строк о том, почему это не поддерживается с MVVM

using GalaSoft.MvvmLight.Command;
using GalaxyCreator.Dialogs.DialogService;
using GalaxyCreator.Model.Json;
using System.Windows;

    namespace GalaxyCreator.Dialogs.JobEditor
    {
        class JobEditorDetailViewModel : DialogViewModelBase
        {
            private JobEditorDetailMemento memento;
            public Job Job { get; set; }

            private RelayCommand<object> _saveCommand = null;
            public RelayCommand<object> SaveCommand
            {
                get { return _saveCommand; }
                set { _saveCommand = value; }
            }

            private RelayCommand<object> _cancelCommand = null;
            public RelayCommand<object> CancelCommand
            {
                get { return _cancelCommand; }
                set { _cancelCommand = value; }
            }

            private JobOrder _selectedOrder;
            public JobOrder SelectedOrder
            {
                get { return _selectedOrder; }
                set
                {
                    if (_selectedOrder == value)
                        return;
                    _selectedOrder = value;
                    RaisePropertyChanged("SelectedOrder");
                }
            }

            public JobEditorDetailViewModel(string message, Job job) : base(message)
            {
                this.memento = new JobEditorDetailMemento(job);
                this.Job = job;
                this._saveCommand = new RelayCommand<object>((parent) => OnSaveClicked(parent));
                this._cancelCommand = new RelayCommand<object>((parent) => OnCancelClicked(parent));
            }

            private void OnSaveClicked(object parameter)
            {
                this.CloseDialogWithResult(parameter as Window, DialogResult.Yes);
            }

            private void OnCancelClicked(object parameter)
            {
                this.Job.Id = memento.Job.Id;
                this.CloseDialogWithResult(parameter as Window, DialogResult.No);
            }
        }
    }

Это сокращенный код xaml для этого подробного представления

<UserControl x:Class="GalaxyCreator.Dialogs.JobEditor.JobEditorDetailView"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:GalaxyCreator.Dialogs.JobEditor"
      xmlns:model="clr-namespace:GalaxyCreator.Model.Json"
      xmlns:util="clr-namespace:GalaxyCreator.Util"    
      xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"             
      mc:Ignorable="d" d:DesignWidth="600">

    <Grid Margin="4">
        <Label Content="Id" />
        <TextBox Text="{Binding Path=Job.Id, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True, ValidatesOnExceptions=True}" />
        <Label Content="Name" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center" />
        <TextBox Text="{Binding Path=Job.Name, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True, ValidatesOnExceptions=True}" Grid.Column="3" x:Name="name" />

            <Button Name="btnSubmit" Content="Save" 
                Command="{Binding SaveCommand}"
                CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"  Margin="0,0,0,5"/>

            <Button Name="btnCancel" Content="Cancel" 
                Command="{Binding CancelCommand}"
                CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}" />
        </StackPanel>
    </Grid>
</UserControl>

Видите, дело в том, что это действительно работает до определенной степени.Как видите, я тестирую только со свойством id.Когда я нажимаю «Отмена», диалоговое окно закрывается и в памяти свойство изменяется.Если дважды щелкнуть ту же строку в моей сетке данных, откроется диалоговое окно со старым значением.ОДНАКО ... свойство на сетке данных изменилось.Таким образом, в таблице данных вы видите мой новый напечатанный идентификатор, а в памяти он содержит старый.Мне нужен способ сообщить о том, что моя сетка данных эй ... пожалуйста, сделайте обновление, свойство изменилось.

Итак, большой вопрос:

  • Могу ли я сойти с рук в этом?вероятно, взломать, а не очистить MVVM (и, честно говоря, после всех этих часов, я начинаю больше не беспокоиться)
  • есть ли способ дать сигнал сетке данных "отрендерить" еще раз с помощьюновые данные в памяти

Ответы [ 2 ]

0 голосов
/ 22 декабря 2018

Ну, как ответил @Ivan Ičin после реализации интерфейса, он работал, но в переводе на MVVM Light это стало

using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;

namespace GalaxyCreator.Model.Json
{
    public class Job : ObservableObject
    {
        private String _id;
        public String Id
        {
            get { return _id; }
            set
            {
                Set(ref _id, value);
            }
        }
    }
}

Также ObservableObject НЕ Сериализуем !!!Поэтому, если вы клонируете свои объекты с помощью проверенного метода сериализации, а затем создаете экземпляр нового объекта с этой строкой, это не может работать.Я использовал взлом json, чтобы получить его, возможно, не самое чистое решение, но так как я все равно работал с json ... почему бы и нет ...

public static T CloneJson<T>(T source)
{
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }
    return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
}

Я рад, что заставил его работать такСпасибо, Иван, но я сильно разочарован количеством шаблонов, которые я должен добавить к своему простому, как мы бы назвали его в объектах java POJO.Я понимаю, что упомянутый пакет FoddyWeavers, который представляет собой шаблонный код вокруг установщиков, очень похож на AspectJ в Java.Однако это был дополнительный уровень сложности, и мне не хотелось добавлять к этому простое приложение.Показать сетку данных, открыть форму в диалоговом окне, отредактировать материал с возможностью отмены изменений.

C #, поскольку простое решение для создания приложений быстрой формы не убедило меня или не впечатлило меня как разработчика java,Я был бы быстрее сделать в JSF или JSP, и это древняя технология

0 голосов
/ 22 декабря 2018

Для вашего второго вопроса, который, как я считаю, по существу отвечает на все ваши вопросы, вы используете событие PropertyChanged класса, который реализует интерфейс INotifyPropertyChanged, чтобы сигнализировать об изменениях, которые должны быть отображены.

Кажется, что выиспользуя некоторый пакет, который использует метод RaisePropertyChanged, чтобы сделать то, что я сказал выше.В настоящее время вы вызываете его только на одном сеттере, поэтому вам нужно реализовать его в нескольких местах.

...