WPF简单绑定问题

时间:2010-07-23 09:30:27

标签: wpf data-binding binding observablecollection

尝试理解WPF的这种绑定过程。

请参阅底部的代码。

在我的“viewmodel”中,查看底部的代码,我有一个可观察的集合,用项目填充listview。这是包含一个名为symbol的路径的路径,用于在组合框中设置所选索引。现在我的问题是我需要在添加到listview之前从另一个列表中填充组合框(一些默认值)。 因为我刚刚开始使用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);
            }
        }
    }

ViewModelContacts:

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;
    }
}

2 个答案:

答案 0 :(得分:4)

按如下方式定义您的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
    }

能够为由某些功能区分的相同实体使用不同的DataTemplates,您需要使用DataTemplateSelector。该技术是继承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. 我们在x:KeyContactDataType.Working

  3. 的资源中创建了2个DataTemplates(ContactDataType.Personal非常重要)
  4. 我们创建了DataTemplateSelector按功能切换模板。

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

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

  7. 注意:更改名称空间的TestCustomTab

答案 1 :(得分:1)

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