簡體   English   中英

WPF通知ComboBox選擇框和下拉列表項未同步且未及時刷新

[英]WPF notified ComboBox selection box and drop-down list item not synchronizing and not refreshing promptly

我希望我的MVVM表單中的組合框在綁定數據更改后立即刷新其顯示當前選定項的選擇框和當前選定項的下拉列表項。 我無法做到這一點。 刷新圖片也很重要。

該示例表單具有2個組合框,其中顯示了預加載的人員,以及一個用於添加人員的按鈕和一個用於更改現有人員的某些數據的按鈕。 單擊該按鈕后, Person.Type字段會隨機更改一點,並在Person.PicFullPath存儲另一個圖片文件字符串。

<Window x:Class="combobox_test__01.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:combobox_test__01"
        mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="420" WindowStartupLocation="CenterScreen">
    <Window.Resources>
        <local:VM x:Key="vm" />

        <DataTemplate x:Key="cboTemplate" >
            <StackPanel Orientation="Horizontal">
                <Border Height="80" Width="80" >
                    <Image>
                        <Image.Source>
                            <PriorityBinding>
                                <Binding Path="PicFullPath"  Mode="TwoWay"/>
                            </PriorityBinding>
                        </Image.Source>
                    </Image>
                </Border>
                <Canvas Height="80" Width="160" >
                    <StackPanel>
                        <TextBlock HorizontalAlignment="Left" Height="16" Text="{Binding Path=Name, Mode=TwoWay}"  />
                        <TextBlock HorizontalAlignment="Left" Height="16" Text="{Binding Path=Type, Mode=TwoWay}"  />
                    </StackPanel>
                </Canvas>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>
    <Window.DataContext>
        <Binding Source="{StaticResource vm}" />
    </Window.DataContext>
    <Grid>
        <ComboBox Name="cbo1" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" Margin="19,11,0,0" VerticalAlignment="Top" Width="159" 
                ItemsSource="{Binding People}" 
                SelectedItem="{Binding SelectedPerson}"  
                DisplayMemberPath="Type" />
        <Button Content="New Row" HorizontalAlignment="Left" Margin="224,11,0,0" VerticalAlignment="Top" Width="142" 
                Command="{Binding NewRowCommand}" />
        <Button Content="Change current row data" HorizontalAlignment="Left" Margin="224,48,0,0" VerticalAlignment="Top" Width="142" 
                Command="{Binding AlterRowCommand}" />
        <ComboBox Name="cbo2" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" Margin="19,130,0,0" VerticalAlignment="Top" Width="159" Height="82" 
            ItemsSource="{Binding People}"
            SelectedItem="{Binding SelectedPerson}" 
            ItemTemplate="{StaticResource cboTemplate}" />
    </Grid>
</Window>

using myAssemblies;
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.ComponentModel;

namespace combobox_test__01
{
    public class VM : INotifyPropertyChanged
    {
        private ObservableCollection<Person> people;
        public ObservableCollection<Person> People
        {
            get { return people; }
            set
            {
                 people = value;
                OnPropertyChanged("People");
            }
        }

        private Person selectedPerson;
        public Person SelectedPerson
        {
            get { return selectedPerson; }
            set
            {
                selectedPerson = value;
                OnPropertyChanged("SelectedPerson");
                People = People;
            }
        }

        private int idx = 0;
        private string[,] newP;
        private string DefaultPic = @"D:\Visual Studio Testing\Icons\user-icon.png",
            NewPic = @"D:\Visual Studio Testing\Small size pictures\mugshot";
        private Random random = new Random();

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
            }
        }

        public VM()
        {
            People = new ObservableCollection<Person>()
            {
                new Person() {Name="Aa", Type="Taa", PicFullPath=DefaultPic},
                new Person() {Name="Bb", Type="Tbb", PicFullPath=DefaultPic},
                new Person() {Name="Cc", Type="Tcc", PicFullPath=DefaultPic}
            };

            newP = new string[4, 3] { { "Dd", "Tdd", "" }, { "Ee", "Tee", "" }, { "Ff", "Tff", "" }, { "Gg", "Tgg", "" } };
        }

        public ICommand AlterRowCommand => new RelayCommandBase(AlterRow);
        private void AlterRow(object parameter)
        {
            //Make SelectedPerson.Type into a string ending with 2 randomish digits
            string fmts, picChoice;
            GetDifferentData(out fmts, out picChoice);
            string s = SelectedPerson.Type.Substring(0,3).PadRight(5);
            SelectedPerson.Type = s + fmts;

            // Use those 2 randomish digits to choose a picture from existing ones
            SelectedPerson.PicFullPath = NewPic + picChoice;

            // Force both setters to execute
            SelectedPerson = SelectedPerson;
        }

        public ICommand NewRowCommand => new RelayCommandBase(NewRow);
        private void NewRow(object parameter)
        {
            string fmts, picChoice;
            GetDifferentData(out fmts, out picChoice);
            People.Add(new Person() { Name = newP[idx, 0], Type = newP[idx++, 1], PicFullPath = NewPic + picChoice });
        }

        private void GetDifferentData(out string fmts, out string picChoice)
        {
            int randomi = random.Next(1, 35);
            fmts = randomi.ToString("D2");
            picChoice = fmts + ".jpg";
        }
    }

    public class Person
    {
        public string Name { get; set; }
        public string Type { get; set; }
        public string PicFullPath { get; set; }
    }
}

這是行為:

運行應用程序
視圖模型處於狀態1
點擊“更改當前行數據”
視圖模型處於狀態2
無明顯變化:我希望選擇框顯示更改后的Type數據和其他圖片

單擊組合框cbo1
下拉列表顯示People.Type的更改。 選擇框仍顯示舊數據。

單擊組合框cbo2
下拉列表顯示People.Type的更改和其他圖片。 選擇框仍顯示舊數據。

再次單擊“更改當前行數據”,而不移動所選項目。

視圖模型處於狀態3。
無明顯變化:撤消下拉列表后,表單看起來與初始化時相同。

單擊組合框cbo1
下拉列表仍顯示People.Type的第一次更改,但People.Type已再次更改。 選擇框仍顯示原始數據。

單擊組合框cbo2
自上次刪除以來,下拉列表從未更改過。 選擇框仍顯示原始數據。

視圖模型中的狀態有3種變化,但是組合框在選擇框中顯示了狀態1,在下拉列表中顯示了狀態2。 我希望他們在兩個地方都顯示State 3。

單擊組合框cbo1並選擇另一個項目,然后選擇第一個項目。 因此,我們已將所選項目移回。
兩個組合框均在選擇框中顯示最新數據
下拉列表已過期。 兩者都顯示狀態2,因此更改選擇並將其更改回並不會更改下拉列表中的顯示數據。

點擊“新行”
視圖模型處於狀態4
無明顯變化:按預期方式-未選擇新人員,因此不會有明顯變化。

單擊組合框
新用戶將顯示在下拉列表中,但第一項仍顯示狀態2。

單擊不會使下拉列表在第一次更改后顯示更改-它們停留在狀態2。

如何使選擇框和下拉列表始終顯示最新數據?

我有解決辦法。 它不是很干凈,但是很簡單並且可以正常工作。 這是經過更新的視圖模型代碼,其中進行了一些小的更改和一個新方法:

using npAssemblies;
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.ComponentModel;

namespace combobox_test__01
{
    public class VM : INotifyPropertyChanged
    {
        private ObservableCollection<Person> people;
        public ObservableCollection<Person> People
        {
            get { return people; }
            set
            {
                 people = value;
                OnPropertyChanged("People");
            }
        }

        private Person selectedPerson;
        public Person SelectedPerson
        {
            get { return selectedPerson; }
            set
            {
                selectedPerson = value;
                OnPropertyChanged("SelectedPerson");
            }
        }

        private int cboPeopleSelectedIndex;
        public int CboPeopleSelectedIndex
        {
            get { return cboPeopleSelectedIndex; }
            set
            {
                cboPeopleSelectedIndex = value;
                OnPropertyChanged("CboPeopleSelectedIndex");
            }
        }

        private int newPidx = 0;
        private string[,] newP;
        private string DefaultPic = @"D:\Visual Studio Testing\Icons\user-icon.png",
            NewPic = @"D:\Visual Studio Testing\Small size pictures\mugshot";
        private Random random = new Random();

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
            }
        }

        public VM()
        {
            People = new ObservableCollection<Person>()
            {
                new Person() {Name="Aa", Type="Taa", PicFullPath=DefaultPic},
                new Person() {Name="Bb", Type="Tbb", PicFullPath=DefaultPic},
                new Person() {Name="Cc", Type="Tcc", PicFullPath=DefaultPic}
            };

            newP = new string[4, 3] { { "Dd", "Tdd", "" }, { "Ee", "Tee", "" }, { "Ff", "Tff", "" }, { "Gg", "Tgg", "" } };
        }

        public ICommand AlterRowCommand => new RelayCommandBase(AlterRow);
        private void AlterRow(object parameter)
        {
            //Make SelectedPerson.Type into a string ending with 2 randomish digits
            string fmts, picChoice;
            GetDifferentData(out fmts, out picChoice);
            string s = SelectedPerson.Type.Substring(0,3).PadRight(5);
            SelectedPerson.Type = s + fmts;
            // Use those 2 randomish digits to choose a picture from existing ones
            SelectedPerson.PicFullPath = NewPic + picChoice;
            // refresh the control the collection is bound to
            ResetBoundItems(SelectedPerson);
        }

        public ICommand NewRowCommand => new RelayCommandBase(NewRow);
        private void NewRow(object parameter)
        {
            string fmts, picChoice;
            int highIdx = People.Count;
            GetDifferentData(out fmts, out picChoice);
            Person newPerson = new Person() { Name = newP[newPidx, 0], Type = newP[newPidx++, 1], PicFullPath = NewPic + picChoice };
            People.Add(newPerson);
            // refresh the control the collection is bound to and select the new row
            ResetBoundItems(newPerson, highIdx);
        }

        private void GetDifferentData(out string fmts, out string picChoice)
        {
            int randomi = random.Next(1, 35);
            fmts = randomi.ToString("D2");
            picChoice = fmts + ".jpg";
        }

        private void ResetBoundItems(Person inPerson, int gotoIndex = -1)
        {
            // refreshes the display of the combobox otherwise it is visually stale
            int idx = gotoIndex;
            if (gotoIndex == -1)
            {
                // a preferred index was not supplied; use the currently selected index
                idx = CboPeopleSelectedIndex;
            }
            // save a copy of the current selected person
            Person tmpP = (Person)inPerson.Clone();
            // save the current ItemsSource
            ObservableCollection<Person> refPC = new ObservableCollection<Person>();
            refPC = People;
            // reset the ItemsSource
            ObservableCollection<Person> tmpPC = new ObservableCollection<Person>();
            People = tmpPC;
            // set it back
            People = refPC;
            // restore the selected person
            SelectedPerson = (Person)tmpP.Clone();
            // select the relevant row
            CboPeopleSelectedIndex = idx;
        }
    }

    public class Person
    {
        public string Name { get; set; }
        public string Type { get; set; }
        public string PicFullPath { get; set; }

        public Person Clone()
        {
            return (Person)this.MemberwiseClone();
        }
    }
}

該視圖需要ComboBoxes具有綁定的SelectedIndex,因此添加

SelectedIndex="{Binding CboPeopleSelectedIndex}" 

到ComboBoxes定義。

新方法ResetBoundItems()處理刷新。 Person類需要一個Clone()方法。 添加時,我使ComboBox選擇新行。 該解決方案也適用於ListBoxes。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM