【问题标题】:WPF simple binding problemWPF简单绑定问题
【发布时间】:2011-03-20 00:25:51
【问题描述】:

试图理解WPF的这个绑定过程。

查看底部的代码。

在我的“viewmodel”中,查看底部的代码,我有一个可观察的集合,它用项目填充列表视图。那是一个包含一个名为符号的路径,用于在组合框中设置选定的索引。现在我的问题是我需要先从另一个列表填充组合框,然后再将其添加到列表视图(一些默认值)。 由于我刚开始使用 WPF,我认为也许您可以在同一个类中使用 2 个不同的 ObservableCollections 来实现这一点,但这不起作用。那么如何在数据模板绑定/添加到列表视图之前使用默认值填充数据模板?

这是我用来填充列表视图的,请参阅底部的 viewmodelcontacts。

我还尝试使用可以在组合框中使用的新 observablecollection 添加另一个类,但我也没有让它工作。 应填充的数据来自我的应用程序中作为资源的 XML 文件。

另一个问题,是否可以向图像添加命令?还是只能从从 button_base 类继承的控件中获得命令?我想在元素旁边使用图像,当用户单击该图像时,他们会删除该元素。

  • 根据下面的答案,是否可以不添加按钮,因为我不想要按钮的感觉(例如悬停和单击时)*

Window.xaml:

<r:RibbonWindow x:Class="Onyxia_KD.Windows.ContactWorkspace"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"        
    Title="Contact card" ResizeMode="NoResize" Height="600" Width="600"
    Background="White">
<r:RibbonWindow.Resources>
    <DataTemplate x:Key="cardDetailFieldTemplate">
        <TextBox Text="{Binding Path=Field}" MinWidth="150"></TextBox>
    </DataTemplate>
    <DataTemplate x:Key="cardDetailValueTemplate">
        <TextBox Text="{Binding Path=Value}" MinWidth="150"></TextBox>
    </DataTemplate>
    <DataTemplate x:Key="cardDetailSymbolTemplate">
         <!-- Here is the problem. Populating this with some default values for each entry  before the selectedIndex is bound from the datasource -->
        <ComboBox SelectedIndex="{Binding Path=Symbol}" DataContext="_vmSettings" ItemsSource="{Binding Symbols}" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Center">
            <ListViewItem Padding="0,3,0,3" Content="{Binding Path=Value}" />                                    
        </ComboBox>
    </DataTemplate>
    <DataTemplate x:Key="cardDetailCategoryTemplate">
        <ComboBox SelectedIndex="{Binding Path=Category}" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Center">
            <!--same as the combobox above but categories instead of symbols-->                
        </ComboBox>
    </DataTemplate>
</r:RibbonWindow.Resources>
...
<ListView ItemsSource="{Binding ContactData}" Foreground="Black" SelectionMode="Single" x:Name="cardDetailList" KeyDown="cardDetailList_KeyDown">
                    <ListView.View>
                        <GridView>
                            <GridViewColumn Header="Category" Width="auto" CellTemplate="{StaticResource cardDetailCategoryTemplate}"></GridViewColumn>
                            <GridViewColumn Header="Field" Width="auto" CellTemplate="{StaticResource cardDetailFieldTemplate}"></GridViewColumn>
                            <GridViewColumn Header="Symbol" Width="70" CellTemplate="{StaticResource cardDetailSymbolTemplate}"></GridViewColumn>
                            <GridViewColumn Header="Value" Width="auto" CellTemplate="{StaticResource cardDetailValueTemplate}"></GridViewColumn>                                
                        </GridView>
                    </ListView.View>                        
                </ListView>

后面的代码:

    private ViewModelContacts _vm;  

    public ContactWorkspace()
    {
        InitializeComponent();

        _vm = new ViewModelContacts();            
        this.DataContext = _vm;

    }

    private void Run_MouseUp(object sender, MouseButtonEventArgs e)
    {
        _vm.AddNewDetail();
    }

    private void Image_MouseUp(object sender, MouseButtonEventArgs e)
    {
        _vm.AddNewDetail();
    }

    private void cardDetailList_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Delete)
        {
            if (MessageBox.Show("Are you sure that you want to delete this?", "Warning!", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
            {
                _vm.RemoveDetail(cardDetailList.SelectedIndex);
            }
        }
    }

ViewModel 联系人:

public ObservableCollection<ContactCardData> ContactData { get; set; }               

    public ViewModelContacts()
    {

        ContactData = new ObservableCollection<ContactCardData>();            
        Populate();
    }

    private void Populate()
    {
        ContactData.Add(new ContactCardData("Test", 0, 0, "Value123"));
        ContactData.Add(new ContactCardData("Test2", 1, 1, "Value1234"));
        ContactData.Add(new ContactCardData("Test3", 2, 2, "Value1235"));
        ContactData.Add(new ContactCardData("Test4", 3, 3, "Value12356"));            
    }

    public void UpdateNode()
    {
        ContactData.ElementAt(0).Value = "Giraff";
    }

    public void AddNewDetail()
    {
        ContactData.Add(new ContactCardData());
    }

    public void RemoveDetail(int position)
    {
        ContactData.RemoveAt(position);
    }

ViewModelContactData:

public class ContactCardData : DependencyObject
{
    public int Category
    {
        get { return (int)GetValue(CategoryProperty); }
        set { SetValue(CategoryProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Category.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CategoryProperty =
        DependencyProperty.Register("Category", typeof(int), typeof(ContactCardData), new UIPropertyMetadata(0));

    public string Field
    {
        get { return (string)GetValue(FieldProperty); }
        set { SetValue(FieldProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Field.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FieldProperty =
        DependencyProperty.Register("Field", typeof(string), typeof(ContactCardData), new UIPropertyMetadata(""));

    public string Value
    {
        get { return (string)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(string), typeof(ContactCardData), new UIPropertyMetadata(""));

    public int Symbol
    {
        get { return (int)GetValue(SymbolProperty); }
        set { SetValue(SymbolProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Symbol.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SymbolProperty =
        DependencyProperty.Register("Symbol", typeof(int), typeof(ContactCardData), new UIPropertyMetadata(0));

    public ContactCardData()
    {
    }

    public ContactCardData(string field, int category, int symbol, string value)
    {
        this.Symbol = symbol;
        this.Category = category;
        this.Field = field;
        this.Value = value;
    }
}

【问题讨论】:

    标签: wpf data-binding binding observablecollection


    【解决方案1】:

    如下定义你的 Window 类(稍后解释):

    <Window x:Class="TestCustomTab.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestCustomTab="clr-namespace:TestCustomTab" Title="Window1" Height="300" Width="300">
            <Window.Resources>
                <DataTemplate x:Key="workingTemplate">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{Binding Name}"/>
                        <ComboBox Grid.Column="1" SelectedIndex="0" ItemsSource="{Binding Symbols}">
                            <ComboBox.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="Auto"/>
                                            <ColumnDefinition Width="*"/>
                                        </Grid.ColumnDefinitions>
                                        <Ellipse Grid.Column="0" Fill="Red" Height="5" Width="5"/>
                                        <TextBlock Grid.Column="1" Text="{Binding}" />
                                    </Grid>
                                </DataTemplate>
                            </ComboBox.ItemTemplate>
                        </ComboBox>
                    </Grid>
                </DataTemplate>
    
                <DataTemplate x:Key="personalTemplate">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{Binding Name}"/>
                        <ComboBox Grid.Column="1" SelectedIndex="0" ItemsSource="{Binding Symbols}">
                            <ComboBox.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="Auto"/>
                                            <ColumnDefinition Width="*"/>
                                        </Grid.ColumnDefinitions>
                                        <Ellipse Grid.Column="0" Fill="Green" Height="5" Width="5"/>
                                        <TextBlock Grid.Column="1" Text="{Binding}" />
                                    </Grid>
                                </DataTemplate>
                            </ComboBox.ItemTemplate>
                        </ComboBox>
                    </Grid>
                </DataTemplate>
    
                <TestCustomTab:ContactDataByTypeTemplateSelector x:Key="contactDataByTypeTemplateSelector"/>
            </Window.Resources>
            <Grid x:Name="grid">        
                <ListView ItemsSource="{Binding ContactDataCollection, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TestCustomTab:Window1}}}">
                    <ListView.View>
                        <GridView>
                            <GridViewColumn Header="Column" Width="200" CellTemplateSelector="{StaticResource contactDataByTypeTemplateSelector}">
    
                            </GridViewColumn>
                        </GridView>
                    </ListView.View>
                </ListView>
            </Grid>
        </Window>
    

    假设您的 ContactData 如下所示:

    public class ContactData : INotifyPropertyChanged
        {
            private string _name;
            private int _homePhone;
            private int _mobilePhone;
            private string _value;
            private ContactDataType _dataType;
    
            public ContactData()
            {
            }
    
            public ContactData(string name, int homePhone, int mobilePhone, string value, ContactDataType dataType)
            {
                _name = name;
                _dataType = dataType;
                _value = value;
                _mobilePhone = mobilePhone;
                _homePhone = homePhone;
            }
    
            #region Implementation of INotifyPropertyChanged
    
            public ContactDataType DataType
            {
                get { return _dataType; }
                set
                {
                    if (_dataType == value) return;
                    _dataType = value;
                    raiseOnPropertyChanged("DataType");
                }
            }
    
            public string Name
            {
                get { return _name; }
                set
                {
                    if (_name == value) return;
                    _name = value;
                    raiseOnPropertyChanged("Name");
                }
            }
    
            public int HomePhone
            {
                get { return _homePhone; }
                set
                {
                    if (_homePhone == value) return;
                    _homePhone = value;
                    raiseOnPropertyChanged("HomePhone");
                }
            }
    
            public int MobilePhone
            {
                get { return _mobilePhone; }
                set
                {
                    if (_mobilePhone == value) return;
                    _mobilePhone = value;
                    raiseOnPropertyChanged("MobilePhone");
                }
            }
    
            public string Value
            {
                get { return _value; }
                set
                {
                    if (_value == value) return;
                    _value = value;
                    raiseOnPropertyChanged("Value");
                    raiseOnPropertyChanged("Symbols");
                }
            }
    
            public ReadOnlyCollection<char> Symbols
            {
                get
                {
                    return !string.IsNullOrEmpty(_value) ? new ReadOnlyCollection<char>(_value.ToCharArray()) : new ReadOnlyCollection<char>(new List<char>());
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            private void raiseOnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
    
            #endregion
        }
    

    它具有ContactDataType类型的DataType属性,即枚举:

    public enum ContactDataType
        {
            Working,
            Personal
        }
    

    如果您需要使用 DataTemplateSelector,您需要使用 DataTemplateSelector 来为相同的实体使用不同的 DataTemplates 来区分某些功能。该技术是从 DataTemplateSelector 继承并覆盖 SelectTemplate 方法。在我们的例子中:

    public class ContactDataByTypeTemplateSelector : DataTemplateSelector
        {
            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                var contactData = item as ContactData;
                var control = container as FrameworkElement;
                if (contactData != null & control != null)
                    switch (contactData.DataType)
                    {
                        case ContactDataType.Working:
                            return control.TryFindResource("workingTemplate") as DataTemplate;
                        case ContactDataType.Personal:
                            return control.TryFindResource("personalTemplate") as DataTemplate;
                        default:
                            return base.SelectTemplate(item, container);
                    }
    
                return base.SelectTemplate(item, container);
            }
        }
    

    这是后面代码中的 Window1 类:

    public partial class Window1 : Window
        {
            private ObservableCollection<ContactData> _contactDataCollection = new ObservableCollection<ContactData>();
    
            public Window1()
            {
                ContactDataCollection.Add(new ContactData("test1", 0, 1, "value1", ContactDataType.Working));
                ContactDataCollection.Add(new ContactData("test2", 0, 1, "value2", ContactDataType.Working));
                ContactDataCollection.Add(new ContactData("test3", 0, 1, "value3", ContactDataType.Working));
                ContactDataCollection.Add(new ContactData("test4", 0, 1, "value4", ContactDataType.Personal));
                ContactDataCollection.Add(new ContactData("test5", 0, 1, "value5", ContactDataType.Personal));
    
                InitializeComponent();
    
            }
    
    
            public ObservableCollection<ContactData> ContactDataCollection
            {
                get { return _contactDataCollection; }
            }
        }
    

    现在解释:

    1. 我们创建了一些需要向用户 (ContactData) 表示的类,并让他拥有功能 - ContactDataType

    2. 我们在资源中为 ContactDataType.WorkingContactDataType.Personal 创建了 2 个 DataTemplates(x:Key 很重要)

    3. 我们创建了DataTemplateSelector 以按功能切换模板。

    4. 在我们的第一个 GridViewColumn 中,我们定义了 CellTemplateSelector 并将我们的 ContactDataByTypeTemplateSelector 绑定到它。

    5. 在运行时,只要集合发生变化ContactDataByTypeTemplateSelector,根据项目特征选择我们的模板,我们可以为任意数量的已定义特征提供任意数量的模板。

    注意:将 TestCustomTab 更改为您的命名空间。

    【讨论】:

    • 您好,感谢您的回复。我似乎没有100%解释自己。我为 listview 和我的代码添加了我的 XAML,但我没有设法开始工作的是符号。我不知道要绑定什么。我想将所有预定义的 xml 内容添加到外部类,并将其用于所有预定义的数据绑定。但是如何将一个组合框同时绑定到 2 个数据上下文?一种用于应用当前选定的索引,另一种用于在组合框显示之前使用默认值填充组合框。
    【解决方案2】:

    对于最后一个问题,您可以使用按钮并将其模板化以包含图像。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-15
      • 1970-01-01
      • 2015-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多