44286


ItemsControl にコンボボックスを配置した後、コンボボックスの ItemsSource と SelectedItem
をほぼ同時に変化させると正常に反映されないという問題にあたっています。

再現コードは以下の通りです。  
最初はコンボボックスの内容はhogeのみで、それが選択されており、5秒後にfugaに変化してそれが自動的に選択されることを意図しています。  
しかし、実際には5秒後にコンボボックスの項目はfugaに変化しますが、無選択状態になります。  
さらに5秒後にfugaが選択されることから、十分に時間をおけばVMからも選択できるということがわかります。

原因、回避方法はありますか。  
再現コードでは INotifyPropertyChanged を自力で実装していますが、実際は ReactiveProperty を使用しています。  
それぞれのコンボボックスの項目は別のものである必要があります。

// xaml

[code]

    <Window ... >
        <Window.DataContext>
            <local:MainWindowContext/>
        </Window.DataContext>
        <ItemsControl ItemsSource="{Binding Path=DdlList}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding Path=Items}" SelectedItem="{Binding Path=Selected}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Window
    
[/code]

// データコンテキスト

[code]

    public class MainWindowContext
    {
        public List<DdlCtx> DdlList { get; private set; }
    
        public MainWindowContext()
        {
            DdlList = new List<DdlCtx>()
            {
                new DdlCtx()
            };
    
            _ = Task.Delay(TimeSpan.FromSeconds(5)).ContinueWith(t =>
            {
                DdlList.First().Update();
            });
    
            _ = Task.Delay(TimeSpan.FromSeconds(10)).ContinueWith(t =>
            {
                DdlList.First().Select();
            });
        }
    }
    
    public class DdlCtx : INotifyPropertyChanged
    {
        public List<string> Items { get; private set; }
        public string Selected { get; set; }
    
        public DdlCtx()
        {
            Items = new List<string>() { "hoge" };
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Items"));
    
            Selected = Items.First();
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Selected"));
    
        }
    
        public void Update()
        {
            Items = new List<string>() { "fuga" };
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Items"));
    
            Selected = Items.First();
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Selected"));
        }
    
        public void Select()
        {
            Selected = Items.First();
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Selected"));
    
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    }
    
[/code]


CC BY-SA 4.0
2018-05-22T07:25:23.423
44151
2018-05-27T04:34:58.743
2018-05-27T04:34:58.743
20885
20885
question
c# wpf reactivex
ItemsControl のアイテムにコンボボックスを配置した際、VMからの変更通知がうまく働かない


Update()メソッドがTask.Delay()からUIスレッドと異なるスレッドで呼び出されることが原因ではないでしょうか?  
端的に書くと、下のようにDispatcherを噛ましてやれば正常に更新されます。  
タイマーの入れ方やスレッドの同期に関しては他にもいろいろなアプローチがあると思いますが、分かりやすい例として。

[code]

    public void Update()
    {
        Application.Current.Dispatcher.Invoke(new Action(() =>
        {
            Items = new List<string>() { "fuga" };
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Items"));
    
            Selected = Items.First();
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Selected"));
        }));
    }
    
[/code]


CC BY-SA 4.0
2018-05-22T14:32:14.597
44164
2018-05-22T14:32:14.597
2947
44151
answer


解決しました。  
実際のコードでは

[code]

    // 初期化
    ReactiveProperty<List<string>> Source = ...;
    ReadOnlyReactiveProperty<List<string>> Items;
    Items = Source.ToReadOnlyReactiveProperty();
    
    // Update (UIスレッド)
    await 時間のかかる処理
    Source.Value = ...
    Selected.Value = ...
    
[/code]

のようにしていました。  
ここで、何らかの理由でプロパティ変更通知がUpdateを抜けた後に発生するために、

Itemsの値変更  
→Selectedの値変更  
→Itemsの通知  
→SelectedがItemsの中から見つからなくなったのでUI側からnullをセット  
→Itemsの通知

という順序でnullがセットされたままになった、と考えられます。

ただItemsをReactivePropertyにして直接変更すると即座に通知してくれる様で、その辺の挙動がわかっていない...

解決策としてはItemsとSelectedを初期化する際にImmediateSchedulerを指定し、値変更が必ずUIスレッドから行われるようにしました。


CC BY-SA 4.0
2018-05-27T04:34:34.053
44286
2018-05-27T04:34:34.053
20885
44151
answer