Как вернуться из целевой модели представления в исходную модель представления с данными, собранными в целевой модели представления, и вызвать метод в исходной модели представления - PullRequest
0 голосов
/ 22 января 2020

У меня есть этот вид (фрагмент) с 2 кнопками. Если я нажму левую кнопку, откроется вид 2. Если я нажму правую кнопку, откроется вид 3. Я использую Caliburn Micro. Следовательно, значение x: Name кнопки - это имя метода модели представления, которое вызывается после нажатия кнопки.

View1:

enter image description here

     <StackPanel Name="PnlButtons" 
                Grid.Row="1"  
                Grid.ColumnSpan="2"
                HorizontalAlignment="Center"
                Orientation="Horizontal"
                Opacity="1">
        <Button x:Name="ArtikelAuswahl" 
                Background="Bisque" 
                Content="Artikel auswählen" 
                Width="170" Height="25"
                FontFamily="Verdana">
        </Button>
        <Button x:Name="SonderAuswahl" 
                Background="BlanchedAlmond" 
                Content="Sonderartikel hinzufügen" 
                Width="170" Height="25"
                FontFamily="Verdana">
        </Button>
    </StackPanel>

Вот 2 метода, которые вызываются после нажатия кнопки. Вы можете видеть, что они имеют одинаковое имя. Теперь люди говорят, что открыть модель внутри модели запрещено. Вот почему я использую экземпляр IWindowManager winmanager внутри своих методов, когда я хочу открыть новое представление. Вместо создания нового экземпляра представления, я создаю новый экземпляр viewmodel! Первый вопрос: это противоречит правилам MVVM?

ViewModel1:

    public class CreateLieferscheinViewModel : Conductor<object>
    {

        private IWindowManager winmanager = new WindowManager();
        public InventurartikelViewModel inventur = new InventurartikelViewModel(); 
        public SonderartikelViewModel sonder = new SonderartikelViewModel();

        public void ArtikelAuswahl()
        {          
            wwinmanager.ShowWindow(inventur, null, null);        
        }

        public void SonderAuswahl()
        {
            winmanager.ShowWindow(sonder,null,null);
        }

        /* ToBeImplemented: Invoke this method once `Artikelliste` is filled!!! */
        public void ArtikellisteUmformen()
        {
            for (int k = 0; k < inventur.Artikelliste.Count; k++)
        {
            Artikelsammlung.Add(new ArtikelModel()); //every selected article will get added to Artikelsammlung
            //get each selected article unfiltered (unformatted)
            Artikelsammlung[k].Bezeichnung = inventur.Artikelliste[k].ToString();
            //Extract the unit out of the Artikel-String
            Artikelsammlung[k].Einheit = Zeichenketten.TextFindenVonBisEnde(Artikelsammlung[k].Bezeichnung, "<", ">");

           //remove "in <Einheit>" from the Artikel-String
            Artikelsammlung[k].Bezeichnung = Zeichenketten.EinheitEntfernen(Artikelsammlung[k].Bezeichnung);
            /* 
             * Bezeichnung and Einheit are now properly formatted...
             */
        }
    }

}

    private ObservableCollection<ArtikelModel> _artikelsammlung;
    public ObservableCollection<ArtikelModel> Artikelsammlung
    {
        get { return _artikelsammlung; }
        set
        {
            _artikelsammlung = value;
            OnPropertyChanged("Artikelsammlung");
        }
    }

Хорошо, теперь давайте скажем, что вызывается ArtikelAuswahl . Благодаря Caliburn Micro, View2 показывает:

View2:

enter image description here

<Window x:Class="Lieferscheine.Views.InventurartikelView"
         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:Lieferscheine.Views"
         xmlns:main="clr-namespace:Lieferscheine"
         xmlns:cal="http://www.caliburnproject.org"
         mc:Ignorable="d" Title="Inventurartikel suchen"
         Height="450" Width="370">
<StackPanel Height="423" VerticalAlignment="Bottom">
    <Label Name="lblArtikelbezeichnung" Content="Artikelbezeichnung:" Margin="20, 20, 20, 0"></Label>
    <TextBox Name="BezText" 
             Width="Auto" 
             Margin="20, 0, 20, 0"
             IsEnabled="{Binding Path=BezEnabled}"
             cal:Message.Attach="[Event KeyUp] = [Action KeyUpBez($executionContext)]">
    </TextBox>

    <Label Name="lblLieferant" Content="Lieferant:" Margin="20, 0, 20, 0"></Label>
    <TextBox Name="LiefText" 
             Width="Auto" 
             Margin="20, 0, 20, 0"
             IsEnabled="{Binding Path=LiefEnabled}"                
             cal:Message.Attach="[Event KeyUp] = [Action KeyUpLief($executionContext)]">
    </TextBox>

    <Button Name="SucheArtikel" 
            Content="Suchen" 
            Width="100" Height="25" 
            Margin="20, 10,240, 10">
    </Button>
    <Button x:Name="GesamteListeAnzeigen" 
            Content="Gesamte Liste anzeigen" 
            Width="150" Height="26" 
            Margin="0, -50, 20, 0" 
            HorizontalAlignment="Right"/>
    <main:MultipleSelectionListBox 
             x:Name="LboxAddArtikel"                
             SelectionMode="Multiple" 
             Width="320" Height="220" 
             Margin="20, 10, 20, 10"
             BindableSelectedItems="{Binding Path=MyCollectionOfSelectedIDs}">
    </main:MultipleSelectionListBox>

    <Button x:Name="FuegeArtikelHinzu" 
            Content="Hinzufügen" 
            Width="100" Height="25">
    </Button>
</StackPanel>

View2 - это данные, привязанные к ViewModel2. Но прежде чем я покажу вам ViewModel2, я хочу показать вам, что я могу сделать в View2:

enter image description here

Я выбираю 3 статьи из списка и нажимаю на Кнопка внизу view2 для добавления этих статей в список:

<Button x:Name="FuegeArtikelHinzu" Content="Hinzufügen" Width="100" Height="25"> </Button>

Статьи добавляются в список по методу FuegeArtikelHinzu в viewmodel2:

ViewModel2:

public class InventurartikelViewModel : Screen
{
    private List<string> _artikelliste = new List<string>();
    public List<string> Artikelliste
    {
        get { return _artikelliste; }
        set
        {
            _artikelliste = value;
            OnPropertyChanged("Artikelliste");
        }
    }

    public bool ArtikellisteUpdated()
    {
        Filled = Artikelliste != null ? true : false;

        return Filled;
    }

    private bool _filled;
    public bool Filled
    {
        get 
        { 
            return _filled; 
        }
        set
        {
            _filled = value;
            OnPropertyChanged("Filled");
            if(_filled == true)
            {
                //TO BE IMPLEMENTED
                //then invoke ViewModelA's method `ArtikellisteUmformen()`
            }
            _filled = false; //I believe I would need to set _filled back 

            //to false to prevent overflow. Otherwise ViewModelA's method 

            //`ArtikellisteUmformen()` would get invoked over and over again because 

            //_filled is always true from now on. Is that correct?
        }

    }
    public void FuegeArtikelHinzu()
    {
        try
        {
            //This adds only the multiple selected items to a list
            var multi = MyCollectionOfSelectedIDs;

            int i = 0;
            foreach (string item in multi)
            {
                Artikelliste.Insert(i, item);
                i++;
            }

            MessageBox.Show("Artikel hinzugefügt!"); //ok, all added... 

            //call `ArtikellisteUpdated()` and set Property `Filled` (=`Artikelliste` is filled) to true
            ArtikellisteUpdated(); //true unless null

        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message, "Zuerst Artikel auswählen!"); //you must select an article first...
        }
    }
}

И вот моя проблема! Мне нужны выбранные статьи в списке artikelliste В МОЕЙ МОДЕЛИ ВИДА1 !!! Но я зашел в тупик. Я слышал, что это решается путем внедрения службы IMessenger, но я не понимаю, как это работает в моем примере. Что мне нужно сделать, чтобы передать artikelliste в ViewModel1 в соответствии с моим примером? Если вы не знакомы с Caliburn Micro, опубликуйте другое решение, с нуля или с фреймворком, я не против. Заранее благодарен за любую помощь!

РЕДАКТИРОВАТЬ:

Теперь я могу получить доступ к ViewModel2 и его Artikelliste свойство в ViewModel1. Тем не менее, я хотел бы вызвать метод ViewModel1 ArtikellisteUmformen(), как только обновится Artikelliste ViewModel2. Как я могу это сделать? Вот что я хотел бы сделать:

Когда заполнено Artikelliste, вы можете вызвать событие в ViewModel2, например, ArtikelListeUpdated. ViewModel1 прослушивает это событие и реагирует на него в случае необходимости. Вам даже не нужно событие, если вам не нужно реагировать на него немедленно.

1 Ответ

1 голос
/ 22 января 2020
  1. Создание экземпляра ViewModel и использование интерфейса WindowManager не относится к MVVM. Вам должно быть хорошо с этим.

  2. Я не знаком с Caliburn Micro Framework. Но что по этому поводу:

    • Содержит artikelliste как свойство в ViewModel2 .
    • Содержит экземпляр ViewModel2 как свойство в ViewModel1 . Вы передаете этот экземпляр при вызове метода WindowManager.ShowWindow .
    • Затем, когда заполнен artikelliste , вы можете вызвать событие для ViewModel2 , например ArtikelListeUpdated . ViewModel1 прослушивает это событие и реагирует на него в случае необходимости. Вам даже не нужно событие, если вам не нужно немедленно реагировать на него.

Редактировать: Пример для последнего шага:

public class ViewModel1 {
    public ViewModel2 ChildVm {get;} = new ViewModel2();

    public ViewModel1() {
        ChildVm.Updated += OnChildUpdated;
    }

    private void OnChildUpdated(object pSender, EventArgs pArgs) {
        // do what is needed
    }
}

public class ViewModel2 {
    public event EventHandler Updated;

    public void DoStuff()
    {
        // do something

        if (Updated != null)
            Updated.Invoke(this, EventArgs.Empty);
    }
}

Имейте в виду, что ViewModel2 имеет внутреннюю ссылку на ViewModel1 в этом случае, поэтому предотвращает сборку мусора ViewModel1.

Я рекомендую ознакомиться с основами EventHandling для C#, прежде чем продолжить работу с вашим проектом: Понимание событий и обработчиков событий в C#

...