WPF простая задача связывания - PullRequest
1 голос
/ 23 июля 2010

Пытаясь понять этот процесс связывания WPF.

См. Код внизу.

В моей "viewmodel", см. Код внизу, у меня есть наблюдаемая коллекцияэто заполнение списка с элементами.Это тот, который содержит путь, называемый символом, для установки выбранного индекса в выпадающем списке.Теперь моя проблема заключается в том, что мне нужно заполнить комбинированный список из другого списка, прежде чем он будет добавлен в список (некоторые значения по умолчанию).Так как я только начал с WPF, я подумал, что, возможно, вы можете использовать 2 разных ObservableCollections в одном классе, чтобы достичь этого, но это не сработало.Итак, как я могу заполнить таблицу данных значениями по умолчанию, прежде чем они будут связаны / добавлены в просмотр списка?

Это то, что я использую для заполнения просмотра списка, см. Viewmodelcontacts внизу.

Я также попытался добавить еще один класс с новой наблюдаемой коллекцией, которую я мог бы использовать в своем комбинированном ящике, но у меня тоже не получалось.Данные, которые должны быть заполнены, поступают из файла 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 ]

4 голосов
/ 23 июля 2010

Определите ваш класс 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>

Предположим, что ваши контактные данные выглядят следующим образом:

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
    }

Имеет свойство DataType типа ContactDataType, которое является enum:

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. Мы создали 2 DataTemplates в ресурсах (x:Key важно) для ContactDataType.Working и ContactDataType.Personal

  3. Мы создали DataTemplateSelector, чтобы иметь возможность переключать шаблоны по функциям.

  4. В нашем первом GridViewColumn мы определили CellTemplateSelector и свяжем с ним наш ContactDataByTypeTemplateSelector.

  5. Во время выполнения всякий раз, когда коллекция изменяется ContactDataByTypeTemplateSelector, выберите нам шаблон на основе функции элемента, и у нас может быть любое количество шаблонов для любого количества определенных функций.

Примечание: измените TestCustomTab для вашего пространства имен.

1 голос
/ 23 июля 2010

Для последнего вопроса вы можете использовать кнопку и шаблон для включения изображения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...