2

私はWPFが初めてです。WPF バインディングを使用して MVVM パターンを理解しようとしています。私は2つのクラスの下にあります

  1. MainWindow.xamal
  2. ビューモデル

    私は3つのコントロールを持っています

    1. ViewModel の Name プロパティを表示するテキストボックス
    2. ViewModel の「ステータス」依存プロパティを表示するテキスト ボックス
    3. 「ViewModel」クラスの「Execute」メソッドを呼び出すボタン。

    ここで、Execute() メソッドは少しかさばるので、デリゲートを作成して非同期で呼び出します。しかし、それでも私のUIはブロックされており、「ステータス」依存プロパティの値を更新していません

以下のクラスを参照してください。

App.xaml.cs

namespace bindingDemo
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            MainWindow mw = new MainWindow();
            ViewModel vm = new ViewModel();

            ///Set data context property of main windows.
            mw.DataContext = vm;
            mw.Show();
        }
    }
}

MainWindow.xaml

<Window x:Class="bindingDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <TextBox Text="{Binding Name, Mode=TwoWay}"   Height="23" HorizontalAlignment="Left" Margin="76,26,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
    <Button Command="{Binding Path=MyCommand}" Content="Button" Height="23" HorizontalAlignment="Left" Margin="76,127,0,0" Name="button1" VerticalAlignment="Top" Width="120" />
    <TextBox Text="{Binding Path=Status}"  Height="23" HorizontalAlignment="Left" Margin="76,55,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" />
</Grid>

ViewModel.cs

    namespace bindingDemo
{
    public class ViewModel : DependencyObject , ICommand
    {
        public string Status
        {
            get { return (string)GetValue(StatusProperty); }
            set { SetValue(StatusProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Status.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StatusProperty =
            DependencyProperty.Register("Status", typeof(string), typeof(ViewModel), new UIPropertyMetadata("In Progress..."));       

        private ICommand _command = null;

        public ViewModel()
        {
            Name = "Default Name";
        }


        public void Execute(object parameter)
        {            
            Action a = new Action(() =>
            {
                ///While this code is being executed, UI gets blocked.
                Console.WriteLine(Name);
                Name = "OK";
                Status = "Connecting to database....";
                Thread.Sleep(2000);
                Status = "Connected to database....";
                Thread.Sleep(2000);
                Status = "Performing validations....";
                Thread.Sleep(2000);
                Status = "Data saved.";

            });

            /// Even if I have invoked operation asynchronously, UI is not getting updated
            /// UI is freezing for 6 seconds and can directly see last 'Status' message on UI
            Dispatcher.BeginInvoke(a, null);            
        }

        public string Name { get; set; }

        public ICommand MyCommand
        {
            get
            {
                return this;
            }
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;
    }
}

誰かがこれについて私を助けることができますか?

よろしく、ヘマント

4

2 に答える 2

4

通常、ViewModel には依存関係プロパティは含まれません。データ バインディングを介して UI を更新できるようにするには、INotifyPropertyChangedインターフェイスを実装する必要があります。
次のように ViewModel を実装してみてください。

public class ViewModel : INotifyPropertyChanged
{
    private string _status;

    public string Status
    {
        get { return _status; }
        set
        {
            if(_status == value)
                return;
            _status = value;

            OnPropertyChanged("Status");
        }
    }

    public event EventHandler<PropertyChangedEventArgs> PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if(handler != null)
            handler(new PropertyChangedEventArgs(propertyName));
    }

    // ...
}

ビューモデルに実装ICommandするのもかなり奇妙に思えます。

于 2013-04-10T06:54:35.117 に答える
2

ここでいくつかのこと:

  1. DependencyPropertyは...まあ、依存関係プロパティを持つクラスです。ビュー モデルの場合は、 を実装しINotifyPropertyChangedます。DependencyObject継承を結び付けますが、意図した使用法ではありません。

  2. でアクションを呼び出してDispatcherおりDispatcher、ディスパッチャー スレッド (この場合は UI スレッド) で関数を実行するために使用する必要があります。ブロックされるのも不思議ではありません。UI スレッドでメソッドを呼び出しています。Dispatcherバックグラウンド タスクから UI にバインドされた値を変更する (たとえば、ある種の進行状況を報告する) 場合に便利です。ロジックを分離し、バックグラウンドで処理を行い、結果を報告する必要があります。

そうは言っても、Executeおそらく次のようになります (C# 5 を使用):

private async Task DoStuff()
{
     await Task.Delay(5000);
    //or drop the async modifier and 'return Task.Delay(5000);'
}

public async void Execute(object parameter)
{
    await DoStuff();
    //Add some checks if it really was 'OK', catch exceptions etc
    Name = "OK";
}

C# 4 (未テスト) の場合:

private Task DoStuff()
{
    return Task.Factory.StartNew(() => Thread.Sleep(5000));
}

public void Execute(object parameter)
{
   DoStuff().ContinueWith(result => Name = "OK", TaskScheduler.FromCurrentSynchronizationContext());
   //Same as above, probably should specify appropriate TaskOptions to run the continuation
   //only when the task was completed successfully.
}
于 2013-04-10T06:54:47.613 に答える