c# - ربط تحرير وسرد تحرير وسرد الورق إلى قائمة مخصصة




wpf data-binding (3)

قمت بتعيين DisplayMemberPath و SelectedValuePath إلى "اسم" ، لذلك أفترض أن لديك فئة PhoneBookEntry مع خاصية عامة اسم.

هل قمت بتعيين DataContext إلى كائن ConnectionViewModel الخاص بك؟

لقد قمت بنسخ الكود وقمت ببعض التعديلات الطفيفة ، ويبدو أنها تعمل بشكل جيد. يمكنني تعيين الخاصية Viewmodels PhoneBookEnty والعنصر المحدد في التغييرات combobox ، ويمكنني تغيير العنصر المحدد في combobox ونماذج العرض يتم تعيين الخاصية PhoneBookEntry بشكل صحيح.

هذا هو محتوى XAML الخاص بي:

<Window x:Class="WpfApplication6.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
<Grid>
    <StackPanel>
        <Button Click="Button_Click">asdf</Button>
        <ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
                  DisplayMemberPath="Name"
                  SelectedValuePath="Name"
                  SelectedValue="{Binding Path=PhonebookEntry}" />
    </StackPanel>
</Grid>
</Window>

وإليكم رمز خلفي:

namespace WpfApplication6
{

    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            ConnectionViewModel vm = new ConnectionViewModel();
            DataContext = vm;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ((ConnectionViewModel)DataContext).PhonebookEntry = "test";
        }
    }

    public class PhoneBookEntry
    {
        public string Name { get; set; }

        public PhoneBookEntry(string name)
        {
            Name = name;
        }

        public override string ToString()
        {
            return Name;
        }
    }

    public class ConnectionViewModel : INotifyPropertyChanged
    {
        public ConnectionViewModel()
        {
            IList<PhoneBookEntry> list = new List<PhoneBookEntry>();
            list.Add(new PhoneBookEntry("test"));
            list.Add(new PhoneBookEntry("test2"));
            _phonebookEntries = new CollectionView(list);
        }

        private readonly CollectionView _phonebookEntries;
        private string _phonebookEntry;

        public CollectionView PhonebookEntries
        {
            get { return _phonebookEntries; }
        }

        public string PhonebookEntry
        {
            get { return _phonebookEntry; }
            set
            {
                if (_phonebookEntry == value) return;
                _phonebookEntry = value;
                OnPropertyChanged("PhonebookEntry");
            }
        }

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

تحرير: جيفز المثال الثاني لا يبدو للعمل ، الأمر الذي يبدو غريبا بعض الشيء بالنسبة لي. إذا قمت بتغيير الخاصية PhonebookEntries على ConnectionViewModel لتكون من نوع ReadOnlyCollection ، يعمل ربط TwoWay الخاصية SelectedValue على combobox بشكل جيد.

ربما هناك مشكلة مع CollectionView؟ لاحظت وجود تحذير في وحدة التحكم في الإخراج:

System.Windows.Data Warning: 50: استخدام CollectionView مباشرة غير مدعوم بشكل كامل. تعمل الميزات الأساسية ، على الرغم من بعض أوجه القصور ، ولكن الميزات المتقدمة قد تواجه أخطاء معروفة. حاول استخدام فئة مشتقة لتجنب هذه المشاكل.

Edit2 (.NET 4.5): يمكن أن يستند محتوى DropDownList على ToString () وليس من DisplayMemberPath ، بينما يحدد DisplayMemberPath العضو للعنصر المحدد والعرض فقط.

لدي ComboBox لا يبدو لتحديث SelectedItem / SelectedValue.

مرتبط ComboBox ItemsSource إلى خاصية على فئة ViewModel يسرد حفنة من إدخالات دفتر الهاتف RAS كـ CollectionView. ثم ربطت (في أوقات منفصلة) على حد سواء SelectedItem أو SelectedValue إلى خاصية أخرى من ViewModel. لقد قمت بإضافة MessageBox إلى الأمر save لتصحيح القيم التي تم تعيينها بواسطة ربط البيانات ، ولكن لم يتم تعيين ربط SelectedItem / SelectedValue .

تبدو فئة ViewModel كالتالي:

public ConnectionViewModel
{
    private readonly CollectionView _phonebookEntries;
    private string _phonebookeEntry;

    public CollectionView PhonebookEntries
    {
        get { return _phonebookEntries; }
    }

    public string PhonebookEntry
    {
        get { return _phonebookEntry; }
        set
        {
            if (_phonebookEntry == value) return;
            _phonebookEntry = value;
            OnPropertyChanged("PhonebookEntry");
        }
    }
}

تتم تهيئة مجموعة _phonebookEntries في المُنشئ من كائن أعمال. The ComboBox XAML يبدو شيء من هذا القبيل:

<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
    DisplayMemberPath="Name"
    SelectedValuePath="Name"
    SelectedValue="{Binding Path=PhonebookEntry}" />

أنا مهتم فقط في قيمة السلسلة الفعلية المعروضة في ComboBox ، وليس أي خصائص أخرى للكائن لأن هذه هي القيمة التي أحتاجها لتمريرها إلى RAS عندما أريد إجراء اتصال VPN ، وبالتالي فإن DisplayMemberPath و SelectedValuePath هما DisplayMemberPath خاصية ConnectionViewModel. يكون ComboBox في ItemsControl على ItemsControl على نافذة تم تعيين DataContext على مثيل ViewModel.

يعرض ComboBox قائمة بالعناصر بشكل صحيح ، ويمكنني اختيار واحد في واجهة المستخدم بدون أي مشكلة. ولكن عندما أقوم بعرض مربع الرسالة من الأمر ، فإن الخاصية PhonebookEntry لا تزال تحتوي على القيمة الأولية في ذلك ، وليس القيمة المحددة من ComboBox. يتم تحديث مثيلات TextBox الأخرى بشكل جيد وعرضها في MessageBox.

ما أنا في عداد المفقودين مع ربط the ComboBox؟ لقد قمت بالكثير من البحث ولا يبدو لي أن أجد أي شيء أفعله خطأ.

هذا هو السلوك الذي أراه ، إلا أنه لا يعمل لسبب ما في سياق خاص بي.

لدي MainWindowViewModel الذي يحتوي على CollectionView من ConnectionViewModels. في التعليمات البرمجية الخاصة بالملف MainWindowView.xaml ، أقوم بتعيين DataContext إلى MainWindowViewModel. يحتوي ItemsControl على ItemsControl منضمة إلى مجموعة من ConnectionViewModels. لدي DataTemplate يحتفظ في ComboBox وكذلك بعض TextBoxes الأخرى. يرتبط TextBoxes مباشرة إلى خصائص ConnectionViewModel باستخدام Text="{Binding Path=ConnectionName}" .

public class ConnectionViewModel : ViewModelBase
{
    public string Name { get; set; }
    public string Password { get; set; }
}

public class MainWindowViewModel : ViewModelBase
{
    // List<ConnectionViewModel>...
    public CollectionView Connections { get; set; }
}

XAML code-behind:

public partial class Window1
{
    public Window1()
    {
        InitializeComponent();
        DataContext = new MainWindowViewModel();
    }
}

ثم XAML:

<DataTemplate x:Key="listTemplate">
    <Grid>
        <ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
            DisplayMemberPath="Name"
            SelectedValuePath="Name"
            SelectedValue="{Binding Path=PhonebookEntry}" />
        <TextBox Text="{Binding Path=Password}" />
    </Grid>
</DataTemplate>

<ItemsControl ItemsSource="{Binding Path=Connections}"
    ItemTemplate="{StaticResource listTemplate}" />

جميع TextBoxes ربط بشكل صحيح ، وينقل البيانات بينها وبين ViewModel دون أي مشكلة. انها فقط ComboBox التي لا تعمل.

أنت على صواب في افتراضك بخصوص فئة PhonebookEntry.

والافتراض الذي أقوم به هو أن DataContext المستخدم من قبل DataTemplate الخاص بي يتم ضبطه تلقائيًا من خلال التسلسل الهرمي للربط ، حتى لا أحتاج إلى ضبطه صراحة لكل عنصر في ItemsControl . هذا سيبدو سخيفًا بالنسبة لي.

هنا تطبيق اختبار يوضح المشكلة ، استناداً إلى المثال أعلاه.

XAML:

<Window x:Class="WpfApplication7.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <DataTemplate x:Key="itemTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBox Text="{Binding Path=Name}" Width="50" />
                <ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
                    DisplayMemberPath="Name"
                    SelectedValuePath="Name"
                    SelectedValue="{Binding Path=PhonebookEntry}"
                    Width="200"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding Path=Connections}"
            ItemTemplate="{StaticResource itemTemplate}" />
    </Grid>
</Window>

وراء الكود :

namespace WpfApplication7
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            DataContext = new MainWindowViewModel();
        }
    }

    public class PhoneBookEntry
    {
        public string Name { get; set; }
        public PhoneBookEntry(string name)
        {
            Name = name;
        }
    }

    public class ConnectionViewModel : INotifyPropertyChanged
    {

        private string _name;

        public ConnectionViewModel(string name)
        {
            _name = name;
            IList<PhoneBookEntry> list = new List<PhoneBookEntry>
                                             {
                                                 new PhoneBookEntry("test"),
                                                 new PhoneBookEntry("test2")
                                             };
            _phonebookEntries = new CollectionView(list);
        }
        private readonly CollectionView _phonebookEntries;
        private string _phonebookEntry;

        public CollectionView PhonebookEntries
        {
            get { return _phonebookEntries; }
        }

        public string PhonebookEntry
        {
            get { return _phonebookEntry; }
            set
            {
                if (_phonebookEntry == value) return;
                _phonebookEntry = value;
                OnPropertyChanged("PhonebookEntry");
            }
        }

        public string Name
        {
            get { return _name; }
            set
            {
                if (_name == value) return;
                _name = value;
                OnPropertyChanged("Name");
            }
        }
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class MainWindowViewModel
    {
        private readonly CollectionView _connections;

        public MainWindowViewModel()
        {
            IList<ConnectionViewModel> connections = new List<ConnectionViewModel>
                                                          {
                                                              new ConnectionViewModel("First"),
                                                              new ConnectionViewModel("Second"),
                                                              new ConnectionViewModel("Third")
                                                          };
            _connections = new CollectionView(connections);
        }

        public CollectionView Connections
        {
            get { return _connections; }
        }
    }
}

إذا قمت بتشغيل هذا المثال ، فسوف تحصل على السلوك الذي أتحدث عنه. يقوم TextBox بتحديث الغرامة الملزمة الخاصة به عند تحريره ، ولكن لا يقوم ComboBox. من المربك للغاية رؤية ما فعلته فعلاً هو تقديم تطبيق ViewModel الأصل.

أعمل حالياً تحت الانطباع بأن عنصرًا مرتبطًا بطفل DataContext له هذا الطفل باعتباره DataContext الخاص به. لا أستطيع العثور على أي وثائق تثبت ذلك بطريقة أو بأخرى.

أي،

نافذة -> DataContext = MainWindowViewModel
..Items -> ملزمة ل DataContext.PhonebookEntries
.... العنصر -> DataContext = PhonebookEntry (مقترن ضمنيًا)

لا أعرف ما إذا كان هذا يفسر افتراضاتي أفضل (؟).

لتأكيد افتراضاتي ، قم بتغيير ارتباط TextBox

<TextBox Text="{Binding Mode=OneWay}" Width="50" />

وهذا سوف تظهر الجذر ربط TextBox (الذي أنا مقارنة DataContext) هو مثيل ConnectionViewModel.


كان لدي ما بدا في البداية مشكلة مماثلة ، ولكن تبين أنه كان بسبب مشكلة التوافق NHibernate / WPF. كانت المشكلة بسبب الطريقة التي يتحقق بها WPF للمساواة بين الكائنات. تمكنت من الحصول على الأشياء الخاصة بي للعمل باستخدام خاصية معرف الكائن في خصائص SelectedValue و SelectedValuePath.

<ComboBox Name="CategoryList"
          DisplayMemberPath="CategoryName"
          SelectedItem="{Binding Path=CategoryParent}"
          SelectedValue="{Binding Path=CategoryParent.ID}"
          SelectedValuePath="ID">

شاهد منشور المدونة من Chester ، و WPF ComboBox - SelectedItem ، SelectedValue ، و SelectedValuePath مع NHibernate ، للحصول على التفاصيل.


لربط البيانات إلى ComboBox

List<ComboData> ListData = new List<ComboData>();
ListData.Add(new ComboData { Id = "1", Value = "One" });
ListData.Add(new ComboData { Id = "2", Value = "Two" });
ListData.Add(new ComboData { Id = "3", Value = "Three" });
ListData.Add(new ComboData { Id = "4", Value = "Four" });
ListData.Add(new ComboData { Id = "5", Value = "Five" });

cbotest.ItemsSource = ListData;
cbotest.DisplayMemberPath = "Value";
cbotest.SelectedValuePath = "Id";

cbotest.SelectedValue = "2";

يبدو ComboData مثل:

public class ComboData
{ 
  public int Id { get; set; } 
  public string Value { get; set; } 
}




combobox