none
Von Window1 auf Komponente von Window2 zugreifen (ShowDialog) RRS feed

  • Frage

  • Hi,

    Ich habe in meinem Projekt eine weitere Window-Komponente implementiert, die mir zu bestimmten Aktionen lediglich einen Status anzeigt. Ich gehe so vor, dass ich die Window-Komponente anzeigen lasse und per async/await unterschiedliche Aufgaben laufen lasse und das Ergebnis auf der geöffneten Window-Komponente anzeigen lasse. Die Anzeige wird übrigens über einTextBlock-Control zur Verfügung gestellt.

    Von der Aufgabenstellung eigentlich kein Problem, solange ich die Show() Methoder der Window-Instanz verwende.

    Ich möchte aber gerne eine Modalität erzwingen und verwende daher ShowDialog(). Das Fenster wird angezeigt, aber keinerlei Aktualisierungen der Komponente oder des TextBlock werden durchgereicht bzw. durchgeführt?! Das Fenster bleibt leer.

    Wie bekomme ich die benötigten Infos auf dem zweiten Fenster aktualisiert?

    Danke

    Freitag, 26. April 2019 22:43

Antworten

  • Hi Arne,
    hier das gleiche Beispiel in C#:

    XAML:

    <Window x:Class="WpfApp1.Window29"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="Window29" Height="450" Width="800" Loaded="Window_Loaded">
      <StackPanel>
        <Button Content="Show Dialog" Click="Button_Click"/>
        <TextBlock Text="{Binding Info}"/>
      </StackPanel>
    </Window>

    Und dazu die Klassen:

    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    namespace WpfApp1
    {
      /// <summary>
      /// Interaction logic for Window29.xaml
      /// </summary>
      public partial class Window29 : Window
      {
        public Window29()
        {
          InitializeComponent();
        }
    
        Window29VM vm = new Window29VM();
    
        private void Window_Loaded(object sender, RoutedEventArgs e) => this.DataContext = vm;
    
        private void Button_Click(object sender, RoutedEventArgs e)
        {
          Window29A wnd = new Window29A();
          wnd.DataContext = vm;
          wnd.ShowDialog();
        }
      }
    
      public class Window29A : Window
      {
        public Window29A() => this.Loaded += Window29A_Loaded;
    
        private TextBox tb = new TextBox();
    
        private void Window29A_Loaded(object sender, RoutedEventArgs e)
        {
          StackPanel sp = new StackPanel();
          this.Content = sp;
          TextBox tb = new TextBox();
          sp.Children.Add(tb);
          Binding b = new Binding("Info");
          b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
          tb.SetBinding(TextBox.TextProperty, b);
        }
      }
    
      public class Window29VM : INotifyPropertyChanged
      {
        private string _info = "<neu>";
        public string Info
        {
          get { return this._info; }
          set { this._info = value; OnPropChanged(); }
        }
        #region  OnPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropChanged([CallerMemberName] string propName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        #endregion
      }
    }


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Montag, 29. April 2019 08:57
  • Hoppla, danke für die Mühe!

    Ich hatte das durch einen Konverter gejagt, der mir das ähnlich ausgespuckt hatte. Lediglich den RaiseEvent konnte er nicht übersetzen, das habe ich dann aber selber angepasst, jedoch führte das alles nicht zum Ziel.

    Ich habe mir mein Konzept aber noch mal durchdacht und gehe jetzt einen etwas anderen Weg, der für mich funktioniert. Evtl. ist das ja eine Hilfe für andere, daher hier kurz meine Version:

    Im Hauptfenster ( MainWindow ) erstelle ich ein Objekt des neuen Window ( ProcessStatusWindow ), setze den Owner auf die Hauptklasse und übergebe die zu verarbeitende Collection:

    private void StartProcessingOrder()
    {
        ProcessStatusWindow StatusUpdate = new ProcessStatusWindow { Owner = this, OrderedItems = this.SummaryItems };
        StatusUpdate.ShowDialog();
    }
    

    Die ganze Verarbeitung erfolgt dann in der Fensterklasse ProcessStatusWindow:

    private async void RunProcess()
    {
        this.txtStatusInfo.Text = "Prüfe Lagerbestände...";
        await Task.Run(() => this.CheckStock());
    
        this.txtStatusInfo.Text = "\r\nFühre Lagerbuchungen durch...";
        await Task.Run(() => this.ProcessStockTransfer());
    
        this.txtStatusInfo.Text = "\r\nErstelle Beleg...";
        await Task.Run(() => this.CreateDocument());
    
        this.txtStatusInfo.Text = "\r\nDrucke Beleg...";
        await Task.Run(() => this.PrintDocument());
    }
    
    
    // start only if window is visible
    private void VisibilityEvent(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (((ProcessStatusWindow)sender).Visibility == Visibility.Visible)
            this.RunProcess();
    }
    
    private void CloseProcessingStatus(object sender, RoutedEventArgs e)
    {
        this.Close();
    }
    

    So ist mein Fenster modal und ich kann die einzelnen Schritte zur Verarbeitung über asyncrone Tasks laufen lassen.

    Ob das jetzt ne geschickte Lösung ist, will ich nicht beurteilen, aber ganz dösig erscheint mir das jedenfalls nicht.

    Aber Danke für Deine Beispiele, die haben mich auf jeden Fall weiter gebracht!

    Gruß Arne

    Montag, 29. April 2019 12:10

Alle Antworten

  • Hi Arne,
    wenn bei Dir der gemeinsame Zugriff auf die gleiche Datenquelle nicht funktioniert, wird ein Design- bzw. Programmierfehler vorliegen. In der folgenden Demo ist ersichtlich, dass auch geänderte Daten in einem mit ShowDialog angezeigten Fenster im Hauptfenster sofort sichtbar sind. Im Hauptfenster (in der Demo Window23) wird in einem TextBlock die Eigenschaft "Info" des DataContextes angezeigt. Der DataContext wird im CodeBehind gesetzt. Mit dem Klick auf den Button wird ein zweites Fenster (Windows23A) per Code erzeugt und dies mit ShowDialog angezeigt. Im zweiten Fenster wird ein StackPanel mit einer TextBox an die Eigenschaft "Info" so gebunden, dass alle Änderungen sofort wirksam werden (UpdateSourceTrigger.PropertyChanged). Als DataContext im zweiten Fenster wird das gleiche Objekt wie im ersten Fenster genutzt. Mit NotifyPropertyChanged werden Änderungen in der TextBox im zweiten Fenster an das erste Fenster "gemeldet".

    XAML des Hauptfensters:

    <Window x:Class="Window23"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="Window23" Height="450" Width="800">
        <StackPanel>
        <Button Content="Show Dialog" Click="Button_Click"/>
        <TextBlock Text="{Binding Info}"/>
      </StackPanel>
    </Window>
    

    Dazu alle Codeteile:

    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    
    Public Class Window23
    
      Dim vm As New Window23VM
    
      Private Sub Window23_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        Me.DataContext = vm
      End Sub
    
      Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
        Dim wnd As New Window23A
        wnd.DataContext = vm
        wnd.ShowDialog()
      End Sub
    
    End Class
    
    Public Class Window23A
      Inherits Window
    
      Private tb As New TextBox
    
      Private Sub Window23A_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        Dim sp As New StackPanel
        Me.Content = sp
        Dim tb As New TextBox
        sp.Children.Add(tb)
        Dim b As New Binding("Info")
        b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
        tb.SetBinding(TextBox.TextProperty, b)
      End Sub
    
    End Class
    
    Public Class Window23VM
      Implements INotifyPropertyChanged
    
      Private _info As String = "<neu>"
      Public Property Info As String
        Get
          Return Me._info
        End Get
        Set(value As String)
          Me._info = value
          OnPropChanged()
        End Set
      End Property
    
      Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
      Private Sub OnPropChanged(<CallerMemberName> Optional propName As String = "")
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
      End Sub
    
    End Class


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Samstag, 27. April 2019 01:44
  • Hi Peter,

    Danke für das Beispiel. Wenn ich das in ein VB.NET Projekt übernehme funktioniert das.

    Ich habe allerdings vergessen zu erwähnen, dass ich in C# unterwegs bin.

    Ich habe das Beispiel entsprechend umgesetzt, passieren tut hier aber leider wieder nichts. Ich schaue mir gerade mal alternativ einige UI-Komponenten für WPF an, die einen Busiy-Indicator liefern.

    Vielen Dank

    Gruß Arne

    Montag, 29. April 2019 05:56
  • Hi Arne,
    hier das gleiche Beispiel in C#:

    XAML:

    <Window x:Class="WpfApp1.Window29"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="Window29" Height="450" Width="800" Loaded="Window_Loaded">
      <StackPanel>
        <Button Content="Show Dialog" Click="Button_Click"/>
        <TextBlock Text="{Binding Info}"/>
      </StackPanel>
    </Window>

    Und dazu die Klassen:

    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    namespace WpfApp1
    {
      /// <summary>
      /// Interaction logic for Window29.xaml
      /// </summary>
      public partial class Window29 : Window
      {
        public Window29()
        {
          InitializeComponent();
        }
    
        Window29VM vm = new Window29VM();
    
        private void Window_Loaded(object sender, RoutedEventArgs e) => this.DataContext = vm;
    
        private void Button_Click(object sender, RoutedEventArgs e)
        {
          Window29A wnd = new Window29A();
          wnd.DataContext = vm;
          wnd.ShowDialog();
        }
      }
    
      public class Window29A : Window
      {
        public Window29A() => this.Loaded += Window29A_Loaded;
    
        private TextBox tb = new TextBox();
    
        private void Window29A_Loaded(object sender, RoutedEventArgs e)
        {
          StackPanel sp = new StackPanel();
          this.Content = sp;
          TextBox tb = new TextBox();
          sp.Children.Add(tb);
          Binding b = new Binding("Info");
          b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
          tb.SetBinding(TextBox.TextProperty, b);
        }
      }
    
      public class Window29VM : INotifyPropertyChanged
      {
        private string _info = "<neu>";
        public string Info
        {
          get { return this._info; }
          set { this._info = value; OnPropChanged(); }
        }
        #region  OnPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropChanged([CallerMemberName] string propName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        #endregion
      }
    }


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Montag, 29. April 2019 08:57
  • Hoppla, danke für die Mühe!

    Ich hatte das durch einen Konverter gejagt, der mir das ähnlich ausgespuckt hatte. Lediglich den RaiseEvent konnte er nicht übersetzen, das habe ich dann aber selber angepasst, jedoch führte das alles nicht zum Ziel.

    Ich habe mir mein Konzept aber noch mal durchdacht und gehe jetzt einen etwas anderen Weg, der für mich funktioniert. Evtl. ist das ja eine Hilfe für andere, daher hier kurz meine Version:

    Im Hauptfenster ( MainWindow ) erstelle ich ein Objekt des neuen Window ( ProcessStatusWindow ), setze den Owner auf die Hauptklasse und übergebe die zu verarbeitende Collection:

    private void StartProcessingOrder()
    {
        ProcessStatusWindow StatusUpdate = new ProcessStatusWindow { Owner = this, OrderedItems = this.SummaryItems };
        StatusUpdate.ShowDialog();
    }
    

    Die ganze Verarbeitung erfolgt dann in der Fensterklasse ProcessStatusWindow:

    private async void RunProcess()
    {
        this.txtStatusInfo.Text = "Prüfe Lagerbestände...";
        await Task.Run(() => this.CheckStock());
    
        this.txtStatusInfo.Text = "\r\nFühre Lagerbuchungen durch...";
        await Task.Run(() => this.ProcessStockTransfer());
    
        this.txtStatusInfo.Text = "\r\nErstelle Beleg...";
        await Task.Run(() => this.CreateDocument());
    
        this.txtStatusInfo.Text = "\r\nDrucke Beleg...";
        await Task.Run(() => this.PrintDocument());
    }
    
    
    // start only if window is visible
    private void VisibilityEvent(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (((ProcessStatusWindow)sender).Visibility == Visibility.Visible)
            this.RunProcess();
    }
    
    private void CloseProcessingStatus(object sender, RoutedEventArgs e)
    {
        this.Close();
    }
    

    So ist mein Fenster modal und ich kann die einzelnen Schritte zur Verarbeitung über asyncrone Tasks laufen lassen.

    Ob das jetzt ne geschickte Lösung ist, will ich nicht beurteilen, aber ganz dösig erscheint mir das jedenfalls nicht.

    Aber Danke für Deine Beispiele, die haben mich auf jeden Fall weiter gebracht!

    Gruß Arne

    Montag, 29. April 2019 12:10