3

ボタンを使用してユーザーが更新できる ComboBox を実装しています。リフレッシュ後にComboBox内にまだ存在する場合、以前に選択したアイテムが自動的に再選択されるようにしようとしています。

MainWindow.xaml:

<ComboBox Canvas.Left="10" Canvas.Top="10" DisplayMemberPath="Name" IsEnabled="{Binding Path=Enabled}" ItemsSource="{Binding Path=Items}" SelectedItem="{Binding Mode=TwoWay, Path=SelectedItem}" Width="379"/>
<Button Content="{x:Static p:Resources.TextRefresh}" Canvas.Right="10" Canvas.Top="10" Click="OnClickButtonRefresh" Width="75"/>

MainWindow.xaml.cs:

public MainWindow()
{
    InitializeComponent();
    DataContext = m_BrowserInstances = new BrowserInstancesViewModel();
}

private void OnClickButtonRefresh(Object sender, RoutedEventArgs e)
{
    m_BrowserInstances.Populate();
}

[現在のバージョンに編集] BrowserInstancesViewModel.cs:

public sealed class BrowserInstancesViewModel : ViewModel
{
    private Boolean m_Enabled;
    public Boolean Enabled
    {
        get { return m_Enabled; }
    }

    private BrowserInstance m_SelectedItem;
    public BrowserInstance SelectedItem
    {
        get { return m_SelectedItem; }
        set
        {
            if (m_SelectedItem != value)
            {
                m_SelectedItem = value;
                NotifyPropertyChanged("SelectedItem");
            }
        }
    }

    private ObservableCollection<BrowserInstance> m_Items;
    public ObservableCollection<BrowserInstance> Items
    {
        get { return m_Items; }
    }

    public BrowserInstancesViewModel()
    {
        Populate();
    }

    private static Func<BrowserInstance, Boolean> Recover(BrowserInstance selectedItem)
    {
        return x =>
        {
            Process currentProcess = x.Process;
            Process selectedProcess = selectedItem.Process;

            if (currentProcess.Id != selectedProcess.Id)
                return false;

            if (currentProcess.MainModule.BaseAddress != selectedProcess.MainModule.BaseAddress)
                return false;

            if (currentProcess.MainWindowTitle != selectedProcess.MainWindowTitle)
                return false;

            return true;
        };
    }

    public void Populate()
    {
        BrowserInstance item = m_SelectedItem;
        List<BrowserInstance> items = new List<BrowserInstance>();

        foreach (Process process in Process.GetProcessesByName("chrome"))
            items.Add(new BrowserInstance(process));

        if (items.Count > 0)
        {
            m_Enabled = true;

            m_Items = new ObservableCollection<BrowserInstance>(items.OrderBy(x => x.Process.Id));

            if (item != null)
                m_SelectedItem = m_Items.SingleOrDefault(Recover(item));

            if (m_SelectedItem == null)
                m_SelectedItem = m_Items[0];
        }
        else
        {
            m_Enabled = false;

            m_Items = new ObservableCollection<BrowserInstance>();
            m_Items.Add(new BrowserInstance());

            m_SelectedItem = m_Items[0];
        }

        NotifyPropertyChanged("Enabled");
        NotifyPropertyChanged("Items");
        NotifyPropertyChanged("SelectedItem");
    }
}

以前に選択したアイテムを取り戻すことができますが、たまにしかありません。以前に選択したアイテムを復元できない場合にデフォルト値 (インデックス 0) を選択する必要がある場合、コードが正しく機能していないようです。

4

1 に答える 1

3

m_SelectedItemで見つかったアイテムに設定する必要がありますSingleOrDefault(Recover(...))

現在、古いインスタンスに設定しています。そのインスタンスはリストに存在しなくなり、明らかにあなたのBrowserInstanceクラスは等価メンバーを実装していません。

現在のコードに基づいてコードを修正します。

if(selectedItem != null)
    m_SelectedItem = m_Items.SingleOrDefault(Recover(selectedItem));
if(m_SelectedItem == null)
    m_SelectedItem = m_Items[0];

アップデート:

アップロードしたコードには 2 つの問題があります。

  1. プロセスがない場合に追加するProcessデフォルト オブジェクトのプロパティの値は です。これにより、で使用される比較コードでa が発生します。前を 次のように変更して修正しますBrowserInstancenullNullReferenceExceptionSingleOrDefault
    if

    if(selectedItem != null && selectedItem.Process != null)
    
  2. Populateメソッドの最後で、PropertyChangedイベントをItems発生させます - コンボボックスの値を更新し、 -SelectedItem選択した項目をユーザーが以前に選択した項目に設定します。
    ここでの問題は、新しい項目リストで以前に選択された項目が見つからないため、が発生したときにWPF が更新SelectedItemされることです。これにより、メソッドで計算した新しい選択項目が効果的に上書きされます。新しく選択したアイテムをにではなくに 割り当てて修正し、イベントが発生した後 にその値をに割り当てます。nullPropertyChangedItemsPopulate
    m_SelectedItemselectedItemSelectedItemPropertyChangedItems

    public void Populate()
    {
        BrowserInstance selectedItem = m_SelectedItem;
        List<BrowserInstance> items = new List<BrowserInstance>();
    
        foreach (Process process in Process.GetProcessesByName("chrome"))
            items.Add(new BrowserInstance(process));
    
        if (items.Count > 0)
        {
            m_Enabled = true;
    
            m_Items = new ObservableCollection<BrowserInstance>(items.OrderBy(x => x.Process.Id));
    
            if (selectedItem != null && selectedItem.Process != null)
                selectedItem = m_Items.SingleOrDefault(x => (x.Process.Id == selectedItem.Process.Id) && (x.Process.MainModule.BaseAddress == selectedItem.Process.MainModule.BaseAddress));
    
            if (selectedItem == null)
                selectedItem = m_Items[0];
        }
        else
        {
            m_Enabled = false;
    
            m_Items = new ObservableCollection<BrowserInstance>();
            m_Items.Add(new BrowserInstance());
    
            selectedItem = m_Items[0];
        }
    
        NotifyPropertyChanged("Enabled");
        NotifyPropertyChanged("Items");
        SelectedItem = selectedItem;
    }
    

等式を適切に実装するBrowserInstanceと、現在選択されている項目を保持する WPF 機能を利用できます。
のコードは次のPopulateように簡略化できます。

public void Populate()
{
    BrowserInstance selectedItem = m_SelectedItem;
    List<BrowserInstance> items = new List<BrowserInstance>();

    foreach (Process process in Process.GetProcessesByName("chrome"))
        items.Add(new BrowserInstance(process));

    m_Enabled = items.Any();
    m_Items = new ObservableCollection<BrowserInstance>(items.OrderBy(x => x.Process.Id));
    if(!m_Enabled)
        m_Items.Add(new BrowserInstance());

    NotifyPropertyChanged("Enabled");
    NotifyPropertyChanged("Items");
    if (SelectedItem == null)
        SelectedItem = m_Items[0];
}

の等価実装はBrowserInstance次のようになります。

public sealed class BrowserInstance : IEquatable<BrowserInstance>
{

    // ...

    public bool Equals(BrowserInstance other)
    {
        if (ReferenceEquals(null, other))
            return false;
        if (ReferenceEquals(this, other))
            return true;
        if (m_Process == null)
        {
            if (other.m_Process == null)
                return true;
            return false;
        }

        if (other.m_Process == null)
            return false;

        return m_Process.Id == other.m_Process.Id && m_Process.MainModule.BaseAddress == other.m_Process.MainModule.BaseAddress;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as BrowserInstance);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return m_Process != null ? ((m_Process.Id.GetHashCode() * 397) ^ m_Process.MainModule.BaseAddress.GetHashCode()) : 0;
        }
    }
}
于 2013-04-23T12:58:29.487 に答える