Как добраться до списка из Xaml`a

WPF
  1. 4 года назад

    Здравствуйте.
    Подскажите пожалуйста есть ли способ передать в конвертер в качестве параметра список, созданный в коде
    К примеру есть код C#:

    ObservableCollection<DbfType> lDataType = new ObservableCollection<DbfType>();

    и Xaml:

    <ComboBox Name="cbDataType" SelectedIndex="{Binding Path=Type, Converter={StaticResource StringToInt}, ConverterParameter=? }"></ComboBox>
  2. Добавлено 4 года назад George777

    Один из вариантов использовать ObjectDataProvider.

    Создав класс с методом возвращающим коллекцию, Вы можете добавить ObjectDataProvider в ресурсы окна, а затем передать его в качестве параметра конвертеру.

    class WorkersTable
        {
            private ObservableCollection<Worker> _workers;
    
            public ObservableCollection<Worker> GetWorkers()
            {
                if(_workers == null)
                {
                    _workers = new ObservableCollection<Worker>();
                    for (int i = 0; i < 5; i++)
                    {
                        var worker = new Worker(i, string.Format("Name {0}", i));
                        _workers.Add(worker);
                    }
                }
    
                return _workers;
            }
        }
    <ObjectDataProvider x:Key="WorkersProvider" ObjectType="{x:Type TestApplication:WorkersTable}" MethodName="GetWorkers"></ObjectDataProvider>
    ...
    <ComboBox SelectedIndex="{Binding Path=Type, Converter={StaticResource StringToInt}, ConverterParameter={StaticResource WorkersProvider}}"></ComboBox>

    Затем в конвертере преобразовывать полученные данные

    var inputParamenter = parameter as ObjectDataProvider;
    if(inputParamenter != null)
    {
       var list = ((WorkersTable) inputParamenter.ObjectInstance).GetWorkers();
    }

    Стоит только соблюдать осторожность с обновлением данных. Если данный ObjectDataProvider использовать в качестве единоличного поставщика данных проблем не должно быть.

    Второй вариант добавить в ресурсы окна объект класса, наследуемого от ObservableCollection, а затем также передавать его в качестве параметра в конвертер - пример с msdn

  3. Спасибо за ответ, но насколько я понял в примерах, приведенных выше инициализируются новый объект и после заполняется либо в конструкторе, либо в методе в данном случае GetWorkers, а как передать уже проинициализированный и заполненный объект?
    или как вариант создать список в ресурсах окна и после заполнять его в коде? у меня это так и не получилось.

  4. Добавлено 4 года назад George777

    Тогда подойдёт второй вариант:
    1) Создайте класс наследуемый от ObservableCollection

    class DbfTypeCollection : ObservableCollection<DbfType>
        {
        }

    2) Затем добавьте в ресурсы окна объект данного класса

    <TestApplication:DbfTypeCollection x:Key="DbfTypeCollectionList"></TestApplication:DbfTypeCollection>
    ...
    SelectedIndex="{Binding Path=Type, Converter={StaticResource StringToInt}, ConverterParameter={StaticResource DbfTypeCollectionList}}"/>

    3) Останется только при инициализации окна или в Loaded связать коллекцию и ресурс

    lDataType = this.Resources["DbfTypeCollectionList"] as DbfTypeCollection ;

    Затем можете добавлять, удалять или изменять элементы в lDataType, при вызове метода Convert конструктора изменения будут учтены.

    var inputParamenter = parameter as DbfTypeCollection ;

    Ну или, на худой конец, можете указать привязку SelectedIndex через код, тогда создавая экземпляр конвертера StringToInt, передавайте в параметр ему всё что угодно. Но это не самый красивый способ. Лучше использовать xaml для таких целей.

  5. Добавлено 4 года назад George777

    Извиняюсь, забыл ещё про кое-что))
    В обход создания нового класса, наследуемого от ObservableCollection, в ресурсах также удобно объявлять экземпляр класса CollectionViewSource

    <CollectionViewSource x:Key="DbfTypeCollectionList"/>

    Затем всё тоже самое:

    var viewSource = this.Resources["DbfTypeCollectionList"] as CollectionViewSource;
    viewSource.Source = lDataType;

    и в методе Covert

    var inputParamenter = parameter as CollectionViewSource;
    var list = inputParamenter.Source;
  6. Спасибо большое!!! Оба варианта рабочие ))) у меня получилось реализовать то что хотел!!!

    Если можно еще небольшой вопрос задам:
    К listview привязан некоторый список, а к textbox привязан текущий элемент этого списка
    Можно ли сделать так что бы при редактировании в Textbox`e изменения отображались и в listview
    реализация класс

    public class DbfType:INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            string nameType;
    
            public string NameType { get { return nameType; } set { nameType = value; OnPropertyChanged(this, "NameType"); } }
    
            public DbfType()
            {
                NameType = "Новый тип данных";
            }
            public DbfType(DbfType copyType)
            {
                if (copyType != null)
                    NameType = copyType.NameType;
            }
            public override string ToString()
            {
                return this.NameType;
            }
    
            private void OnPropertyChanged(object sender, string propertyName)
            {
                if (this.PropertyChanged != null)
                    PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
            }
        }

    Реализация списка

    public class DbfTypesCollection : ObservableCollection<DbfType>
        {        
            public DbfTypesCollection():base(){ }
        }

    Реализация привязки

    <ListView Name="lvDataTypes">
                            <ListView.View>
                                <GridView>
                                    <GridViewColumn Header="Типы данных" Width="250"/>
                                </GridView>
                            </ListView.View>
                        </ListView>
                        <Grid Grid.Column="1" DataContext="{Binding ElementName=lvDataTypes, Path=SelectedItem}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="auto"/>
                                <RowDefinition Height="auto"/>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="auto"/>
                            </Grid.RowDefinitions>
                            <StackPanel Grid.ColumnSpan="3">
                                <TextBlock>Тип данных</TextBlock>
                                <TextBox LostFocus="TextBox_LostFocus" Text="{Binding Path=NameType}"></TextBox>
                            </StackPanel>
    
                            <Button Name="btnAddDataType" Click="ChangedDataType" Grid.Row="1" Style="{StaticResource btnStyleDataType}">Добавить</Button>
                            <Button Name="btnCopyDataType" Click="ChangedDataType" Grid.Column="1" Grid.Row="1" Style="{StaticResource btnStyleDataType}">Копировать</Button>
                            <Button Name="btnDelDataType" Click="ChangedDataType" Grid.Column="2" Grid.Row="1" Style="{StaticResource btnStyleDataType}">Удалить</Button>
    
                            <Button Name="btnSaveDataType" Click="SaveDataType" Grid.Column="3" Grid.Row="3" Style="{StaticResource btnStyle}">Сохранить</Button>
                        </Grid>

    Сейчас для обновления я использую lvDataTypes.Items.Refresh() после того как textbox теряет фокус. У этого метода есть один небольшой недостаток если попробовать передать фокус listview (lvDataTypes) после редактирования, то с первого раза фокус не передается т.к. видимо происходит обновление данных. Мне бы хотелось знать есть ли способ обойти этот эффект? Заранее признателен вам.

  7. Добавлено 4 года назад George777

    В WPF есть очень удобная реализация данной проблемы!))
    Во время привязки TextBox-a к контексту данных, Вы можете указать когда и как TextBox должен обновлять источник данных.
    У биндинга есть следующие доступные свойства: Mode и UpdateSourceTriger. Первый отвечает в какую сторону осуществляется привязка, а второй - в какой момент элемент привязки должен обновлять свой источник, в случае если он изменит получаемые данные.
    В Вашем случае это должно выглядеть так:

    <TextBox Text="{Binding Path=NameType, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"></TextBox>

    Mode=TwoWay указывает на то, что данные должны как приходить от ListView так и передаваться назад от TextBox-a. UpdateSourceTrigger=LostFocus указывает на то, что данные обновятся в ListView, после того как TextBox потеряет фокус.
    Свойство UpdateSourceTriger также принимает значение PropertyChanged, позволяющее изменять источник данных сразу же после изменения свойства Text у TextBox-a.
    Данный способ позволит Вам воздержаться от использования события LosFocus у элемента TextBox.

    Более подробно читайте сдесь: Привязка, стили и команды WPF

  8. <TextBox Text="{Binding Path=NameType, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"></TextBox>

    Я пробовал так делать. Почему-то это дело не работает. Если не сложно подскажите в чем может быть проблема?

  9. Странно. Хотелось бы взглянуть тогда на полную разметку элемента ListView и на то как вы привязываете его к списку.
    С классом никаких видимых проблем нет, как и с разметкой Grid-a содержащего Ваш TextBox.
    Если текст TextBox-a, при выборе элемента в ListView, меняется без использования событий, тогда и обратная связь должна отрабатывать.

  10. В приведенном выше примере была полная разметка ListView и реализация классов, я все скопировал как есть из проекта.
    Создание списка самое обычное

    DbfTypesCollection lDataType = new DbfTypesCollection();

    Заполнение происходит из xml файла посредствам статического метода который сделал я(если надо могу его реализацию выложить)

    WorkerXml.LoadTypeXml(lDataType);

    Привязка происходит после заполнения списка

    lvDataTypes.ItemsSource = lDataType;

    В TextBox`e текст, как и положено, меняется при выборе элемента в ListView и даже если отредактировать элемент и выбрать после этот же элемент то в TextBox`e будут отредактированные данные, а в ListView отображаются старые данные. Видимо изменение списка lDataType все-таки происходит, а ListView почему-то обновляться не хочет.

  11. Тогда всё понятно!))

    Найти это сообщение JackK В приведенном выше примере была полная разметка ListView

    Меня насторожило то, что у GridViewColumn в GridView отсутствует привязка к полю NameType.

    <GridViewColumn Header="Типы данных" Width="250" DisplayMemberBinding="{Binding NameType}"/>

    Из-за его отсутствия xaml подставляет значение возвращаемое ToString() - а оно у Вас переписано.
    Используйте явную привязку к полю NameType и всё должно быть OK))

  12. Спасибо!!! и правда все работает как надо!!! )))
    Очень вам признателен!

или зарегистрируйтесь чтобы ответить