Binding ListBox & amp; ObservableCollection [дубликат]

Первый шаг - выяснить, какой рубин вызывается: which ruby

Ваша система говорит: /usr/bin/ruby

Это НЕ прокладка, используемая rbenv, которая ( на MacOS) должно выглядеть так: /Users/<username>/.rbenv/shims/ruby

Прокладка - это фактически сценарий, который действует как перенаправление на версию рубина, которую вы установили. rbenv global 1.9.3 rbenv local --unset rbenv shell --unset Я рекомендую, чтобы при устранении неполадок вы удаляли конкретную «локальную» версию проекта, а также версию оболочки с оболочкой и просто проверяли с помощью параметра «глобальной» версии, который определяется в текстовом файле в ~ / .rbenv / verion, который будет просто номером версии «1.9.3» в вашем случае. Вы можете сделать «ls -laG» в корне вашей папки проекта (а не в домашней папке), чтобы убедиться, что там больше нет файла «.ruby-version».

Вы можете использовать «rbenv версии ", чтобы определить, какая версия rbenv установлена ​​для использования (и расположение и имя файла, который устанавливает это).

rbenv versions

НИ ОДНА ИЗ ЭТОГО ВОПРОСОВ: пока вы не правильно установите путь.

Используйте это, чтобы убедиться, что ваш MacOS будет подчиняться вам: eval "$(rbenv init -)"

Далее следуют: which ruby

Чтобы убедиться это выглядит так: /Users//.rbenv/shims/ruby

Затем добавьте строку в свой профиль, чтобы она запускалась каждый раз при открытии нового окна терминала: ~/.bash_profile eval "$(rbenv init -)"

Есть и другие способы изменения пути, не стесняйтесь заменить любой из них, а не запускать rbenv init.

ПРИМЕЧАНИЕ: переустановите Rails с помощью: gem install rails

Если вы пытались для запуска Ruby on Rails, вам нужно сначала запустить все это, а затем снова установить рельсы. Ранняя установка Rails будет использовать жестко закодированный путь к неправильному рубину, а некоторые другие вещи будут не в том месте, поэтому просто установите драгоценный камень еще раз.

P. S. Если ваш MacOS не будет подчиняться вам (* упомянуто выше), вам, возможно, придется найти другой способ изменить свой путь, но вряд ли это будет проблемой, потому что «Mac просто работают»;)

27
задан caseklim 21 June 2012 в 17:52
поделиться

8 ответов

Попробуйте создать свойство IsSelected для каждого из ваших элементов данных и привязать ListBoxItem.IsSelected к этому свойству

<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
44
ответ дан Rachel 19 August 2018 в 02:54
поделиться
  • 1
    @Rachel - что вы думаете о добавлении таких атрибутов, как IsSelected для моделирования объектов? Я сделал это и немного запутался, особенно когда один и тот же экземпляр модельного объекта появляется в нескольких местах (в дереве). Когда один выбран, все они будут выбраны. Это не всегда желательно. Также мне кажется, что модель «загрязнена» View stuff. Каков ваш опыт? – paul 13 March 2013 в 11:59
  • 2
    Это работает только в том случае, если ListBox не виртуализует. – Eduardo Brites 27 June 2013 в 16:52
  • 3
    @EduardoBrites. Не имеет значения, сохраняется ли ваше значение IsSelected на DataContext – Rachel 27 June 2013 в 16:54
  • 4
    @hypehuman Вероятно, это связано с виртуализацией, где пользовательский интерфейс будет отображать только видимые объекты (и несколько дополнительных для буфера прокрутки) и просто изменять DataContext позади элементов. Таким образом, в вашем SelectAll вам нужно убедиться, что все элементы данных выбраны, потому что цикл через все элементы пользовательского интерфейса не является точным, потому что создаются только видимые элементы. – Rachel 3 November 2016 в 15:07
  • 5
    @hypehuman Это всегда вариант, но помните, что у него может быть проблема с производительностью, если у вас большой набор данных. – Rachel 3 November 2016 в 15:23

Я продолжал искать легкое решение для этого, но не повезло.

Решение, которое имеет Rachel, хорошо, если у вас уже есть свойство Selected на объекте в вашем ItemsSource. Если вы этого не сделаете, вам нужно создать модель для этой бизнес-модели.

Я пошел другим путем. Быстрый, но не идеальный.

В вашем ListBox создайте событие для SelectionChanged.

<ListBox ItemsSource="{Binding SomeItemsSource}"
         SelectionMode="Multiple"
         SelectionChanged="lstBox_OnSelectionChanged" />

Теперь внесите это событие в код, расположенный за вашей страницей XAML.

private void lstBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var listSelectedItems = ((ListBox) sender).SelectedItems;
    ViewModel.YourListThatNeedsBinding = listSelectedItems.Cast<ObjectType>().ToList();
}

Тада. Сделано.

Это было сделано с помощью преобразования SelectedItemCollection в список .

1
ответ дан AzzamAziz 19 August 2018 в 02:54
поделиться

Я не мог заставить решение Рейчел работать так, как я этого хотел, но я нашел ответ Sandesh о создании пользовательского свойства зависимости , чтобы отлично работать для меня. Мне просто пришлось написать аналогичный код для ListBox:

public class ListBoxCustom : ListBox
{
    public ListBoxCustom()
    {
        SelectionChanged += ListBoxCustom_SelectionChanged;
    }

    void ListBoxCustom_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        SelectedItemsList = SelectedItems;
    }

    public IList SelectedItemsList
    {
        get { return (IList)GetValue(SelectedItemsListProperty); }
        set { SetValue(SelectedItemsListProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemsListProperty =
       DependencyProperty.Register("SelectedItemsList", typeof(IList), typeof(ListBoxCustom), new PropertyMetadata(null));

}

В моей модели просмотра я просто ссылался на это свойство, чтобы получить список, выбранный мной.

16
ответ дан Community 19 August 2018 в 02:54
поделиться
  • 1
    Мне нравится этот ответ, но я бы, вероятно, изменил код как таковой: pastebin.com/YTccwmxG – maxp 24 January 2018 в 09:27
  • 2
    В виду модель не воспитание собственности .. можете ли вы, пожалуйста, поделиться xaml тоже – Moumit 31 March 2018 в 18:28
  • 3
    @Moumit, похоже, подходит для одностороннего связывания, но может ли он быть адаптирован для использования с ObservableCollection & lt; T & gt; и сделал два пути? – Wobbles 10 August 2018 в 21:37

Не удовлетворенными ответами, я пытался найти один сам ... Ну, похоже, это больше похоже на хак, а затем на решение, но для меня это хорошо работает. Это решение использует MultiBindings особым образом. Сначала это может показаться тонкой кода, но вы можете использовать его с минимальными усилиями.

Сначала я реализовал «IMultiValueConverter»

public class SelectedItemsMerger : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        SelectedItemsContainer sic = values[1] as SelectedItemsContainer;

        if (sic != null)
            sic.SelectedItems = values[0];

        return values[0];
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return new[] { value };
    }
}

и контейнер SelectedItems Contraper / Wrapper:

public class SelectedItemsContainer
{
    /// Nothing special here...
    public object SelectedItems { get; set; }
}

Теперь мы создаем привязку для нашего ListBox.SelectedItem (Singular). Примечание. Вам необходимо создать статический ресурс для «Конвертера». Это можно сделать один раз для каждого приложения и повторно использовать для всех списков, которым нужен конвертер.

<ListBox.SelectedItem>
 <MultiBinding Converter="{StaticResource SelectedItemsMerger}">
  <Binding Mode="OneWay" RelativeSource="{RelativeSource Self}" Path="SelectedItems"/>
  <Binding Path="SelectionContainer"/>
 </MultiBinding>
</ListBox.SelectedItem>

В ViewModel я создал контейнер, с которым я могу привязываться. Важно инициализировать его новым (), чтобы заполнить его значениями.

    SelectedItemsContainer selectionContainer = new SelectedItemsContainer();
    public SelectedItemsContainer SelectionContainer
    {
        get { return this.selectionContainer; }
        set
        {
            if (this.selectionContainer != value)
            {
                this.selectionContainer = value;
                this.OnPropertyChanged("SelectionContainer");
            }
        }
    }

И все. Может быть, кто-то видит некоторые улучшения? Что вы думаете об этом?

0
ответ дан Fresch 19 August 2018 в 02:54
поделиться

Вот еще одно решение. Это похоже на ответ Бена , но привязка работает двумя способами. Хитрость заключается в обновлении выбранных элементов ListBox при изменении связанных элементов данных.

public class MultipleSelectionListBox : ListBox
{
    public static readonly DependencyProperty BindableSelectedItemsProperty =
        DependencyProperty.Register("BindableSelectedItems",
            typeof(IEnumerable<string>), typeof(MultipleSelectionListBox),
            new FrameworkPropertyMetadata(default(IEnumerable<string>),
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBindableSelectedItemsChanged));

    public IEnumerable<string> BindableSelectedItems
    {
        get => (IEnumerable<string>)GetValue(BindableSelectedItemsProperty);
        set => SetValue(BindableSelectedItemsProperty, value);
    }

    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        base.OnSelectionChanged(e);
        BindableSelectedItems = SelectedItems.Cast<string>();
    }

    private static void OnBindableSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is MultipleSelectionListBox listBox)
            listBox.SetSelectedItems(listBox.BindableSelectedItems);
    }
}

К сожалению, я не смог использовать IList в качестве типа BindableSelectedItems. Это отправлено null в свойство моей модели представления, тип которого IEnumerable<string>.

Вот XAML:

<v:MultipleSelectionListBox
    ItemsSource="{Binding AllMyItems}"
    BindableSelectedItems="{Binding MySelectedItems}"
    SelectionMode="Multiple"
    />

Есть одна вещь, о которой следует помнить. В моем случае из представления можно удалить ListBox. По какой-то причине это приводит к тому, что свойство SelectedItems изменяется на пустой список. Это, в свою очередь, приводит к изменению свойства модели представления в пустой список. В зависимости от вашего варианта использования это может быть нежелательно.

0
ответ дан redcurry 19 August 2018 в 02:54
поделиться

Выключает привязку флажка к свойству IsSelected и помещению текстового блока и флажка в панель стека делает трюк!

-1
ответ дан user953710 19 August 2018 в 02:54
поделиться

Это было серьезной проблемой для меня, некоторые из ответов, которые я видел, были либо слишком хакерскими, либо требовали сброса значения свойства SelectedItems, нарушающего любой код, связанный со свойствами OnCollectionChanged. Но мне удалось получить работоспособное решение, изменив сбор непосредственно и в качестве бонуса он даже поддерживает SelectedValuePath для коллекций объектов.

public class MultipleSelectionListBox : ListBox
{
    internal bool processSelectionChanges = false;

    public static readonly DependencyProperty BindableSelectedItemsProperty =
        DependencyProperty.Register("BindableSelectedItems",
            typeof(object), typeof(MultipleSelectionListBox),
            new FrameworkPropertyMetadata(default(ICollection<object>),
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBindableSelectedItemsChanged));

    public dynamic BindableSelectedItems
    {
        get => GetValue(BindableSelectedItemsProperty);
        set => SetValue(BindableSelectedItemsProperty, value);
    }


    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        base.OnSelectionChanged(e);

        if (BindableSelectedItems == null || !this.IsInitialized) return; //Handle pre initilized calls

        if (e.AddedItems.Count > 0)
            if (!string.IsNullOrWhiteSpace(SelectedValuePath))
            {
                foreach (var item in e.AddedItems)
                    if (!BindableSelectedItems.Contains((dynamic)item.GetType().GetProperty(SelectedValuePath).GetValue(item, null)))
                        BindableSelectedItems.Add((dynamic)item.GetType().GetProperty(SelectedValuePath).GetValue(item, null));
            }
            else
            {
                foreach (var item in e.AddedItems)
                    if (!BindableSelectedItems.Contains((dynamic)item))
                        BindableSelectedItems.Add((dynamic)item);
            }

        if (e.RemovedItems.Count > 0)
            if (!string.IsNullOrWhiteSpace(SelectedValuePath))
            {
                foreach (var item in e.RemovedItems)
                    if (BindableSelectedItems.Contains((dynamic)item.GetType().GetProperty(SelectedValuePath).GetValue(item, null)))
                        BindableSelectedItems.Remove((dynamic)item.GetType().GetProperty(SelectedValuePath).GetValue(item, null));
            }
            else
            {
                foreach (var item in e.RemovedItems)
                    if (BindableSelectedItems.Contains((dynamic)item))
                        BindableSelectedItems.Remove((dynamic)item);
            }
    }

    private static void OnBindableSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is MultipleSelectionListBox listBox)
        {
            List<dynamic> newSelection = new List<dynamic>();
            if (!string.IsNullOrWhiteSpace(listBox.SelectedValuePath))
                foreach (var item in listBox.BindableSelectedItems)
                {
                    foreach (var lbItem in listBox.Items)
                    {
                        var lbItemValue = lbItem.GetType().GetProperty(listBox.SelectedValuePath).GetValue(lbItem, null);
                        if ((dynamic)lbItemValue == (dynamic)item)
                            newSelection.Add(lbItem);
                    }
                }
            else
                newSelection = listBox.BindableSelectedItems as List<dynamic>;

            listBox.SetSelectedItems(newSelection);
        }
    }
}

Связывание работает так же, как вы ожидали бы, что MS сделана сама :

<uc:MultipleSelectionListBox 
    ItemsSource="{Binding Items}" 
    SelectionMode="Extended" 
    SelectedValuePath="id" 
    BindableSelectedItems="{Binding mySelection}"
/>

Он не был тщательно протестирован, но прошел проверку первого взгляда. Я пытался использовать его повторно, используя динамические типы в коллекциях.

0
ответ дан Wobbles 19 August 2018 в 02:54
поделиться
16
ответ дан Community 30 October 2018 в 14:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: