Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WPF, Silverlight Новый топик    Ответить
 Присоединяемое событие ListBoxItem.Selected  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Кто может объяснить почему для кнопок работает присоединяемое событие Button.Click, а для ListBoxItem.Selected - нет?
Пример XAML окна
        <StackPanel Button.Click="Button_Click">
            <Button Content="Кнопка 1"/>
            <Button Content="Кнопка 2"/>
            <Button Content="Кнопка 3"/>
            <ListBox ListBoxItem.Selected="ListBoxItem_Selected">
                <ListBoxItem Content="Строка 1"/>
                <ListBoxItem Content="Строка 2"/>
                <ListBoxItem Content="Строка 3"/>
            </ListBox>
            <TextBlock x:Name="myTB" />
        </StackPanel>
Обработчики в коде окна
        private void Button_Click(object sender, RoutedEventArgs e) 
            => myTB.Text = "Событие Button.Click "+((Button)e.OriginalSource).Content;
 
        private void ListBoxItem_Selected(object sender, RoutedEventArgs e) 
            => myTB.Text = "Событие ListBoxItem_Selected " + ((ListBoxItem)e.OriginalSource).Content;
9 окт 18, 11:48    [21699065]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19959
Eld Hasp
присоединяемое событие
Что за новый зверь? Есть маршрутизируемое событие


Событие Selected есть у Selector, а ListBoxItem не является селектором
<ListBox Selected="ListBox_Selected">
9 окт 18, 15:35    [21699412]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Shocker.Pro, но есть же событие ListBoxItem.Selected. И если записать, так
            <ListBox>
                <ListBoxItem Content="Строка 1" Selected="ListBoxItem_Selected"/>
                <ListBoxItem Content="Строка 2" Selected="ListBoxItem_Selected"/>
                <ListBoxItem Content="Строка 3" Selected="ListBoxItem_Selected"/>
            </ListBox>
То работает. По документации эта запись равносильно предыдущей, так же как и для кнопок. В редакторе при вводе XAML кода ListBox выходит подсказка с этим присоединённым свойством. Но оно не работает. Ошибок тоже не выдаёт.
Я вот и понять не могу в чём дело?
9 окт 18, 17:01    [21699497]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Shocker.Pro
Событие Selected есть у Selector, а ListBoxItem не является селектором
<ListBox Selected="ListBox_Selected">
При такой записи при сборке проекта выдаёт ошибку "CS1061 C# "ListBox" не содержит определения для "Selected", и не удалось найти доступный метод расширения "Selected", принимающий тип "ListBox" в качестве первого аргумента (возможно, пропущена директива using или ссылка на сборку)."
9 окт 18, 17:10    [21699513]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 4606
Shocker.Pro
Что за новый зверь?

Не, не новый: Attached Events Overview
Вот это:
<StackPanel Button.Click="Button_Click">

неявно добавляет в скомпилированный код вот такую штуку:
[DebuggerNonUserCode]
[EditorBrowsable(EditorBrowsableState.Never)]
[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target)
{
  if (connectionId == 1)
  {
    ((StackPanel)target).AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(this.Button_Click));
  }
  else
  {
    this._contentLoaded = true;
  }
}

ну, а для ListBoxItem это не работает потому, что листбокс в своем статическом конструкторе для себя переопределяет ряд событий, в т.ч. MouseUp, которое ответственно в т.ч. за формирование события Click у кнопки.
9 окт 18, 17:38    [21699542]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Сон Веры Павловны
ну, а для ListBoxItem это не работает потому, что листбокс в своем статическом конструкторе для себя переопределяет ряд событий, в т.ч. MouseUp, которое ответственно в т.ч. за формирование события Click у кнопки.
Зачем тогда оставили это в синтаксисе ListBox, в подсказках к нему? Только в заблуждения вводят.

А что с событием ListBox.Selected? Почему оно в редакторе, в подсказках есть, а при сборке выдаёт ошибку?
9 окт 18, 18:02    [21699572]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 4606
Eld Hasp
Сон Веры Павловны
ну, а для ListBoxItem это не работает потому, что листбокс в своем статическом конструкторе для себя переопределяет ряд событий, в т.ч. MouseUp, которое ответственно в т.ч. за формирование события Click у кнопки.

А что с событием ListBox.Selected? Почему оно в редакторе, в подсказках есть, а при сборке выдаёт ошибку?

А, я думал, впрос про почему Button.Click на ListBoxItem не отрабатывает.
Приаттаченный к листбоксу ListBoxItem.Selected не отрабатывает при выборе ListBoxItem по той причине, что ListBoxItem унаследован от Visual, и генерируемые им routed events (которым в т.ч. является событие Selected) распространяются по визуальному дереву. А элементы, сгенерированные в ItemsControl (от которого унаследован ListBox), не являются частью визуального дерева самого ItemsControl, т.е. ListBoxItem, и ListBox - это разные визуальные дереья, поэтому событие просто не может всплыть до ListBox.
Иллюстрация:
<Grid>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <ListBox ListBoxItem.Selected="Selector_OnSelected">
    <ListBoxItem Content="Строка 1"/>
    <ListBoxItem Content="Строка 2"/>
    <ListBoxItem Content="Строка 3"/>
  </ListBox>
  <StackPanel Grid.Row="1" ListBoxItem.Selected="StackPanel_OnSelected">
    <ListBoxItem
      x:Name="InsideStackPanel"
      Content="Inside StackPanel"
      Selected="ListBoxItem_OnSelected"/>
  </StackPanel>
  <Button
    Grid.Row="2"
    Content="test"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    Click="Button_Click" />
</Grid>

public partial class MainWindow
{
  public MainWindow()
  {
    InitializeComponent();
  }

  void Button_Click(object sender, RoutedEventArgs e)
  {
    InsideStackPanel.IsSelected = true;
  }

  void Selector_OnSelected(object sender, RoutedEventArgs e)
  {
    MessageBox.Show("StackPanel.ListBoxItem.Selected");
  }

  void StackPanel_OnSelected(object sender, RoutedEventArgs e)
  {
    MessageBox.Show("StackPanel.ListBoxItem.Selected");
  }

  void ListBoxItem_OnSelected(object sender, RoutedEventArgs e)
  {
    MessageBox.Show("ListBoxItem.Selected");
  }
}

- пли клике на нижнюю кнопку у ListBoxItem внутри StackPanel свойство IsSelected становится равно true, после чего вполне нормально отрабатывает ListBoxItem_OnSelected, и после него StackPanel_OnSelected, т.к. в этом случае ListBoxItem вполне находится внутри визуального дерева StackPanel.
В общем, читайте общие сведения про routed events и деревья.
Eld Hasp
Зачем тогда оставили это в синтаксисе ListBox, в подсказках к нему? Только в заблуждения вводят.

Потому что механизм attached events является общим для всех элементов, унаследованных от UIElement, он определен внутри UIElement, и понятия не имеет о нюансах в своих многочисленных наследниках.
P.S. Опять налицо какие-то странные манипуляции с тем, что в WPF делается легко и просто стандартными методами (в данном случае биндингом свойств). Мне за 8 лет работы с WPF ни разу не понабились attached events. Команды, присоединяемые к событиям через ACB и EventTrigger'ы, мне понадобились раза три от силы, и в очень экзотических случаях.
10 окт 18, 07:14    [21700009]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Roman Mejtes
Member

Откуда: г. Пермь
Сообщений: 3171
есть логическое дерево и визуальное, учитывайте этот момент.
правильнее писать не Button.Click, а ButtonBase.Click

не могу понять, чего автор пытается добиться :)
может имеет смысл использовать команды и CommandBinding?
сами RoutedEvent'ы для внутренней реализации контролов, а не прикладного функционала.
У селектора существует свойство SelectionChanged, использовать нужно его, а не городить какую то фигню, но лично я даже так не делаю, потому что вопрос выделения и списка выделенных элементов обычно лежит на представлении списка и требуется только в VM
10 окт 18, 12:08    [21700283]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Roman Mejtes
не могу понять, чего автор пытается добиться :)
Пытаюсь обучиться. При чтении натыкаюсь на что-то повторяю, пытаюсь изменить. Всплывает непонятное. И "добиваюсь" понимая - но порой не доходит сразу.
Roman Mejtes
но лично я даже так не делаю, потому что вопрос выделения и списка выделенных элементов обычно лежит на представлении списка и требуется только в VM
А если это нужно только для взаимодействия элементов внутри окна. Изменения в данных это не влечёт за собой. По выбранному значению в одном элементе надо "подсветить" значения в другом. Как тогда лучше сделать? Нужно ли для этого привлекать VM? Что посоветуете?
Сон Веры Павловны
В общем, читайте общие сведения про routed events и деревья.
Читал, но похоже плохо. Буду дальше штудировать. Просто мне в голову даже не пришёл вариант, что вложенный элемент может быть из другого дерева. Спасибо за внимание.
Сон Веры Павловны
Опять налицо какие-то странные манипуляции с тем, что в WPF делается легко и просто стандартными методами (в данном случае биндингом свойств)
Это скорее от незнания. Читаю - но знания появляются значительно медленнее чем накапливается информация из прочитанного. Поэтому в голове от дальнейшего чтения - бардак. Пытаюсь как-то применить прочитанное чтобы разобраться в этом бардаке.
Что касается данного примера, то событие Selected лучше обработать в привязке свойства SelectedItem родительского ListBox. Я правильно Вас понял?
Сон Веры Павловны
Команды, присоединяемые к событиям через ACB и EventTrigger'ы, мне понадобились раза три от силы, и в очень экзотических случаях.
Roman Mejtes
может имеет смысл использовать команды и CommandBinding?
А тогда такой вопрос. Два ListBox. При наведении курсора в первом (событие MouseEnter) надо изменять доступный список значений во втором ListBox. У меня это сделано опять таки через EventSetter в стиле для вложенных ListBoxItem. А как правильно такое сделать? Без использования EventSetter?
10 окт 18, 21:02    [21701006]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 4606
Eld Hasp
А тогда такой вопрос. Два ListBox. При наведении курсора в первом (событие MouseEnter) надо изменять доступный список значений во втором ListBox. У меня это сделано опять таки через EventSetter в стиле для вложенных ListBoxItem. А как правильно такое сделать? Без использования EventSetter?

Зачем здесь MouseEnter? Чтобы все дергалось при каждом чихе мыши?
Я так понимаю, что требуются два зависимых списка, и набор значений второго зависит от выбранного значения в первом - обычное отношение master-detail. Это все можно сделать через один биндинг к выбранному значению в первом списке, отображающем элементы, сами содержащие список:
public partial class MainWindow
{
  public MainWindow()
  {
    InitializeComponent();
    DataContext = new TypesModel();
  }
}

public class TypesModel : INotifyPropertyChanged
{
  public TypesModel()
  {
    MemberTypes = ClassMembers.GetMembers(typeof(Window)).ToList();
    SelectedMemberType = MemberTypes.First();
  }

  public IReadOnlyCollection<ClassMembers> MemberTypes { get; }

  ClassMembers _selectedMemberType;
  public ClassMembers SelectedMemberType
  {
    get => _selectedMemberType;
    set
    {
      _selectedMemberType = value;
      OnPropertyChanged();
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

public enum MemberType
{
  [Description("Конструктор")]
  Constructor,
  [Description("Метод")]
  Method,
  [Description("Свойство")]
  Property,
  [Description("Поле")]
  Field,
  [Description("Событие")]
  Event
}

public class ClassMembers
{
  public Type Type { get; }
  public MemberType MemberType { get; }
  public ClassMembers(Type type, MemberType memberType)
  {
    Type = type;
    MemberType = memberType;
    DisplayName = (Attribute.GetCustomAttribute(typeof(MemberType).GetField(memberType.ToString())
      , typeof(DescriptionAttribute)) as DescriptionAttribute)?.Description;
    Func<IEnumerable<MemberInfo>> members;
    switch(MemberType)
    {
      case MemberType.Constructor:
        members = Type.GetConstructors;
        break;
      case MemberType.Method:
        members = Type.GetMethods;
        break;
      case MemberType.Property:
        members = Type.GetProperties;
        break;
      case MemberType.Event:
        members = Type.GetEvents;
        break;
      case MemberType.Field:
        members = ()=>Type.GetFields(BindingFlags.Static | BindingFlags.Public);
        break;
      default:
        throw new ArgumentException($"Unsupported MemberType value ({MemberType})");
    }
    Members = members().Select(mi => mi.Name).OrderBy(s => s).ToList();
  }

  public string DisplayName { get; }

  public IReadOnlyCollection<string> Members { get; }

  public override bool Equals(object obj)=>
    obj is ClassMembers cm && cm.Type==Type && cm.MemberType==MemberType;

  public override int GetHashCode() =>
    ((Func<int[], int>) (hashes =>
        hashes.Aggregate(hashes.Length, (current, t) => unchecked(current * 314159 + t))))
      (new[] {Type.GetHashCode(), MemberType.GetHashCode()});

  public static IEnumerable<ClassMembers> GetMembers(Type type) =>
    from MemberType mt in Enum.GetValues(typeof(MemberType)) select new ClassMembers(type, mt);
}

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition />
    <ColumnDefinition />
  </Grid.ColumnDefinitions>
  <ListBox
    Margin="5"
    ItemsSource="{Binding MemberTypes}"
    SelectedItem="{Binding SelectedMemberType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    DisplayMemberPath="DisplayName" />
  <ListBox
    Grid.Column="1"
    Margin="5"
    ItemsSource="{Binding SelectedMemberType.Members}" />
</Grid>
11 окт 18, 08:11    [21701156]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Сон Веры Павловны
Зачем здесь MouseEnter? Чтобы все дергалось при каждом чихе мыши?
Спасибо за внимание!
Организовано так: при наведении мыши на элемент (MouseEnter) в первом ListBox - во втором отображается список элементов второго уровня в режиме подсказки. Пользователь просматривает его, если это нужный список - он выбирает этот список и, при выходе из первого ListBox, во втором отображается список соответствующий выбранному из первого (SelectedItem). Я поэтому и говорю, что обработка события MouseEnter по сути имеет только вспомогательную, визуальную функцию. На выбор данных это не влияет.
Как методологически верно будет разделить в этом случае функции между View и VM? Где и как правильнее производить вывод информации для второго ListBox в режиме подсказки? Что можно использовать вместо прямой обработке в коде события MouseEnter?
Первоначально, я делал даже, два отдельных ListBox (получалось всего три ListBox): один для отображения выбранного списка, второй для режима подсказки. Но так как по сути они отображают сходные данные и которые не нужны одновременно, решил совместить эти функции в одном ListBox.
Интересует, в первую очередь, сама методология разделения функций в рамках MVVM.
11 окт 18, 11:46    [21701370]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
vb_sub
Member

Откуда:
Сообщений: 468
Eld Hasp,
похожая тема
11 окт 18, 12:05    [21701406]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 4606
Eld Hasp
Где и как правильнее производить вывод информации для второго ListBox в режиме подсказки?

Там, где обычно выводятся подсказки - в тултипе:
<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition />
    <ColumnDefinition />
  </Grid.ColumnDefinitions>
  <ListBox
    Margin="5"
    ItemsSource="{Binding MemberTypes}"
    SelectedItem="{Binding SelectedMemberType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    DisplayMemberPath="DisplayName">
    <ListBox.ItemContainerStyle>
      <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="ToolTip">
          <Setter.Value>
            <ToolTip MaxHeight="200">
              <ItemsControl ItemsSource="{Binding Members}" />
            </ToolTip>
          </Setter.Value>
        </Setter>
      </Style>
    </ListBox.ItemContainerStyle>
  </ListBox>
  <ListBox
    Grid.Column="1"
    Margin="5"
    ItemsSource="{Binding SelectedMemberType.Members}" />
</Grid>
11 окт 18, 12:12    [21701427]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Сон Веры Павловны
Там, где обычно выводятся подсказки - в тултипе:
Да, действительно так лучше смотрится. Спасибо!
vb_sub
похожая тема
Спасибо за совет!
Посмотрел, но там не много другое. Там обрабатывается выбор элементов. В моём случае больше подходит совет Сон Веры Павловны - использовать тултип.
11 окт 18, 13:48    [21701574]     Ответить | Цитировать Сообщить модератору
 Re: Присоединяемое событие ListBoxItem.Selected  [new]
Eld Hasp
Member

Откуда:
Сообщений: 178
Сон Веры Павловны, есть ещё одно место где использую триггера - цвет выделения в ListBox.
Но мы, уже сильно отклонились от начальной темы, чтобы не было путаницы - начну новую.
11 окт 18, 13:50    [21701581]     Ответить | Цитировать Сообщить модератору
Все форумы / WPF, Silverlight Ответить