Обновление привязки данных только для интерфейса при запуске из метода внутри ViewModel - PullRequest
0 голосов
/ 03 июня 2018

Желаемый поток моего проекта:

  1. Нажатие кнопки на пользовательском интерфейсе запускает запуск таймера.
  2. Каждый тик таймера изменяет строку, привязанную к тексту TextBox.
  3. Пользовательский интерфейс обновляет новую строку автоматически.

В течение долгого времени я думал, что у меня возникла проблема с привязкой к данным, потому что пользовательский интерфейс не обновлялся, но потом я обнаружил, вызвал ли я запуск таймера через представлениеМодель, а не внешне через код главного окна, обновил пользовательский интерфейс.Однако это нежелательное поведение, и я пытаюсь понять, почему это так.

Полный просмотр (TextBlock Text="{Binding FileName}" - это обновляемое свойство):

<Window x:Class="MaterialDesignTest.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"
    mc:Ignorable="d"
    xmlns:ns="clr-namespace:MaterialDesignTest"
    xmlns:local="clr-namespace:MaterialDesignTest"
    Title="MainWindow" Height="350" Width="525"
    xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
    TextElement.Foreground="{DynamicResource MaterialDesignBody}"
    TextElement.FontWeight="Regular"
    TextElement.FontSize="13"
    TextOptions.TextFormattingMode="Ideal" 
    TextOptions.TextRenderingMode="Auto"        
    Background="{DynamicResource MaterialDesignPaper}"
    FontFamily="{DynamicResource MaterialDesignFont}">

<materialDesign:DialogHost Identifier="RootDialog" Loaded="DialogHost_Loaded">
    <Grid>
        <StackPanel>
            <Button Width="100" x:Name="SearchRestore" Margin="0 150 0 0" Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}" materialDesign:DialogHost.DialogClosingAttached="SearchRestore_OnDialogClosing" 
                Content="Restore" Click="SearchRestore_Click">
                <Button.CommandParameter>
                        <StackPanel Margin="10">
                        <TextBlock Text="Restoring..." HorizontalAlignment="Center" Margin="0 0 0 15"/>
                        <TextBlock Text="{Binding FileName}" HorizontalAlignment="Center">
                            <TextBlock.DataContext>
                                <ns:ProgressViewModel />
                            </TextBlock.DataContext>
                        </TextBlock>
                        <Button Name="CircleButton" Margin="0 50 0 0" Style="{StaticResource MaterialDesignFloatingActionButton}" IsHitTestVisible="False"
                        materialDesign:ButtonProgressAssist.IsIndicatorVisible="{Binding IsSaving}"
                        materialDesign:ButtonProgressAssist.Value="{Binding SaveProgressButton}">
                            <materialDesign:PackIcon Height="24" Width="24" Foreground="White">
                                <materialDesign:PackIcon.Style>
                                    <Style TargetType="materialDesign:PackIcon" BasedOn="{StaticResource {x:Type materialDesign:PackIcon}}">
                                        <Setter Property="Kind" Value="CloudSync" />
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding IsSaveComplete}" Value="True">
                                                <Setter Property="Kind" Value="Check" />
                                                <DataTrigger.EnterActions>
                                                    <BeginStoryboard>
                                                        <Storyboard>
                                                            <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.8" />
                                                        </Storyboard>
                                                    </BeginStoryboard>
                                                </DataTrigger.EnterActions>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </materialDesign:PackIcon.Style>
                            </materialDesign:PackIcon>
                        </Button>
                    </StackPanel>
                </Button.CommandParameter>
            </Button>
        </StackPanel>
    </Grid>
</materialDesign:DialogHost>

Модель полного просмотра (комментирование KickOffProgressTimer();, приводящее к тому, что пользовательский интерфейс больше не обновляет свойство FileName ):

    using MaterialDesignThemes.Wpf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace MaterialDesignTest
{
    public class ProgressViewModel : INotifyPropertyChanged
    {
    public event PropertyChangedEventHandler PropertyChanged;

    System.Windows.Forms.Timer progressTimer;

    private string _fileName;
    public string FileName
    {
        get { return _fileName; }
        set
        {
            if (value != _fileName)
            {
                _fileName = value;
                OnPropertyChanged("FileName");
            }
        }
    }

    private double _saveProgressButton;
    public double SaveProgressButton
    {
        get { return _saveProgressButton; }
        set { this.MutateVerbose(ref _saveProgressButton, value, RaisePropertyChanged()); }
    }

    private bool _isSaveComplete;
    public bool IsSaveComplete
    {
        get { return _isSaveComplete; }
        private set { this.MutateVerbose(ref _isSaveComplete, value, RaisePropertyChanged()); }
    }

    private bool _isSaving;
    public bool IsSaving
    {
        get { return _isSaving; }
        private set { this.MutateVerbose(ref _isSaving, value, RaisePropertyChanged()); }
    }

    int progress = 0;
    int cycles = 0;
    public ProgressViewModel()
    {
        KickOffProgressTimer();
    }
    public void KickOffProgressTimer()
    {
        progressTimer = new System.Windows.Forms.Timer();
        progressTimer.Tick += new EventHandler(progressTimerTick);
        progressTimer.Interval = 140;
        progressTimer.Start();
    }

    private async void progressTimerTick(object sender, EventArgs e)
    {

        if (progress < 100 && cycles < 2)
        {
            if (progress == 99)
            {
                cycles++;
                progress = 0;
            }

            FileName = SelectRandomString();

            IsSaveComplete = false;
            IsSaving = true;
            progress++;
            SaveProgressButton = progress;
        }
        else
        {
            IsSaveComplete = true;
            IsSaving = false;
            progressTimer.Stop();
            progressTimer.Enabled = false;
            SaveProgressButton = 0;

            await NonBlockingDelay(1750);

            DialogHost.CloseDialogCommand.Execute(null, null);
        }

    }
    async Task NonBlockingDelay(int value)
    {
        await Task.Delay(value);
    }



    private Action<PropertyChangedEventArgs> RaisePropertyChanged()
    {
        return args => PropertyChanged?.Invoke(this, args);
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    static string SelectRandomString()
    {
        var random = new Random();
        var filenames = new List<string>(Directory.GetFiles(@"C:\Windows\System32\"));
        int index = random.Next(filenames.Count);
        return (filenames[index]);
    }
}

}

Логика таймера запуска в коде главного окна:

private void CircleButtonClick(object sender, RoutedEventArgs e)
    {
        ProgressViewModel pvm = new ProgressViewModel();
        CircleButton.DataContext = pvm;
        pvm.KickOffProgressTimer();
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...