简体   繁体   中英

C# WPF Update Status bar text and progress from another window

I have a Main window named "wpfMenu" With a status bar which contains a text block and progress bar. The status bar needs to be updated from methods which are running on separate windows launched from the Main window (only one window open at any time).

Preferably I would like to pass the min, max, progress, text values to a class called "statusUpdate" to update the progress but i have no idea where to begin and any examples of updating progress bars I've come across are running on the same window.

Here is my code for the Status bar so far

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Custom="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon" x:Class="Mx.wpfMenu"
    Title="Mx - Menu" Height="600" Width="1000" Background="#FFF0F0F0" Closed="wpfMenu_Closed">
<Grid>
    <StatusBar x:Name="sbStatus" Height="26" Margin="0,0,0,0" VerticalAlignment="Bottom" HorizontalAlignment="Stretch">
        <StatusBar.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="4*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                </Grid>
            </ItemsPanelTemplate>
        </StatusBar.ItemsPanel>
        <StatusBarItem>
            <TextBlock Name="sbMessage" Text="{Binding statusUpdate.Message}"/>
        </StatusBarItem>
        <StatusBarItem Grid.Column="1">
            <ProgressBar Name="sbProgress" Width="130" Height="18" Minimum="0" Maximum="100" IsIndeterminate="False"/>
        </StatusBarItem>
    </StatusBar>
</Grid>

The code for my class is

    public class statusUpdate : INotifyPropertyChanged
{
    private string _message;
    public event PropertyChangedEventHandler PropertyChanged;

    public statusUpdate()
    {

    }

    public statusUpdate (string value)
    {
        this._message = value;
    }

    public string Message
    {
        get { return _message; }
        set
        {
            _message = value;
            OnPropertyChanged("Message");
        }
    }

    void OnPropertyChanged(string _message)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(_message));
        }
    }
}

There are several steps to this, but they're all well documented elsewhere. It might seem like a complex process, but it's something you'll do over and over in WPF.

You're right to store all the settings in a class. However this class needs to implement INotifyPropertyChanged and raise a PropertyChanged event in every property setter.

using System.ComponentModel;

public class StatusUpdate : INotifyPropertyChanged
{
    private string message;
    public event PropertyChangedEventHandler PropertyChanged;

    public StatusUpdate()
    {
    }

    public string Message
    {
        get { return this.message; }
        set
        {
            this.message = value;
            this.OnPropertyChanged("Message");
        }
    }

    void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Then you can make it a public property of your code-behind class, and bind your progress bar properties to it.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
    }

    public StatusUpdate Status { get; set; } = new StatusUpdate();

    private void PlayCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    public void PlayCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        this.Status.Message = "Play";
    }

    public void StopCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        this.Status.Message = "Stop";
    }
}

Then you can pass a reference to the same class to the child forms, and when they set any of the properties, WPF will catch the event and update the GUI.

Let me know if you can't find an example for any of those steps.

Here's a version of your XAML with the binding and buttons I used for the above example:

<Window x:Class="WpfProgressBar.MainWindow"
    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:WpfProgressBar"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
    <CommandBinding Command="Play" Executed="PlayCommand_Executed" CanExecute="PlayCommand_CanExecute" />
    <CommandBinding Command="Stop" Executed="StopCommand_Executed" />
</Window.CommandBindings>
<Grid>
    <StackPanel>
        <Button Content="Play" Command="Play" />
        <Button Content="Stop" Command="Stop" />
    </StackPanel>
    <StatusBar Height="26" Margin="0,0,0,0" VerticalAlignment="Bottom" HorizontalAlignment="Stretch">
        <StatusBar.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="4*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                </Grid>
            </ItemsPanelTemplate>
        </StatusBar.ItemsPanel>
        <StatusBarItem>
            <TextBlock Text="{Binding Status.Message, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, NotifyOnSourceUpdated=True}"/>
        </StatusBarItem>
        <StatusBarItem Grid.Column="1">
            <ProgressBar Width="130" Height="18" Minimum="0" Maximum="100" IsIndeterminate="False"/>
        </StatusBarItem>
    </StatusBar>
</Grid>
</Window>

NB, I wouldn't normally do command bindings like this, but didn't want to get into the complications of adding RelayCommand

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM