Как передать ссылку на другой элемент управления в качестве параметра IValueConverter? - PullRequest
1 голос
/ 17 сентября 2009

Я связываю некоторые бизнес-объекты с WPF ItemsControl. Они отображаются с использованием пользовательской реализации IValueConverter, используемой для создания геометрии для объекта Path в DataTemplate, как показано здесь:

<ItemsControl x:Name="Display" 
              Background="White"
              HorizontalAlignment="Stretch" 
              VerticalAlignment="Stretch"
              ItemsSource="{Binding ElementName=ViewPlaneSelector, 
                                    Path=SelectedItem.VisibleElements}" 
           >
    <ItemsControl.Resources>
        <!-- This object is just used to get around the fact that ConverterParameter 
        can't be a binding directly (it's not a DependencyProperty on a DependencyObject -->
        <this:GeometryConverterData 
            x:Key="ConverterParameter2"
            Plane="{Binding ElementName=ViewPlaneSelector, 
                            Path=SelectedItem}" />
        <DataTemplate DataType="{x:Type o:SlenderMember}">
            <Path Stroke="Blue" StrokeThickness=".5"
              Data='{Binding Converter={StaticResource SlenderMemberConverter}, 
                            ConverterParameter={StaticResource ConverterParameter2}}'
              ToolTip="{Binding AsString}">
            </Path>
        </DataTemplate>
    </ItemsControl.Resources>
</ItemsControl>

Обратите внимание, что элементы для ItemsControl извлекаются из свойства SelectedItem.VisibleElements ViewPlaneSelector (ComboBox). Мне нужен тот же ViewPlaneSelector.SelectedItem в SlenderMemberConverter, чтобы выяснить, как отобразить этот элемент. Я пытаюсь получить ссылку на него в конвертере, создав промежуточный объект GeometryConverterData в разделе Ресурсы. Этот объект существует исключительно для решения проблемы невозможности привязки напрямую к свойству ConverterParameter (как упоминалось в комментариях). Вот код для класса GeometryDataConverter:

class GeometryConverterData : FrameworkElement {
    public static readonly DependencyProperty PlaneProperty =
        DependencyProperty.Register("Plane", typeof(ViewPlane), 
            typeof(GeometryConverterData), null, ValidValue);

    public static bool ValidValue(object o){
        return true;
    }

    public ViewPlane Plane {
        get{
            return GetValue(PlaneProperty) as ViewPlane;
        }set{
            SetValue(PlaneProperty, value);
        }
    }
}

Я добавил функцию ValidValue для отладки, чтобы увидеть, как это свойство связывало его. Это только и всегда устанавливается в ноль. Я знаю, что ViewPlaneSelector.SelectedItem не всегда является нулевым, поскольку ItemsControl имеет элементы, и его элементы извлекаются из одного и того же свойства в одном и том же объекте ... так что же дает? Как я могу получить ссылку на этот ComboBox в моем ValueConverter.

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

Ответы [ 4 ]

1 голос
/ 29 сентября 2009

Извините, но вы делаете это неправильно.

Весь ваш пост на форуме пахнет одним кодом за другим.
1. Вы используете преобразователи значений для преобразования бизнес-объекта в его визуальный аналог. Это кодовый запах в моей книге.
2. Вы пытаетесь отправить элемент управления пользовательского интерфейса в бизнес-логику.
3. Честно говоря, кто-то здесь сказал «MultiBindings» (и не только потому, что он прав), моя голова взорвалась, и теперь там, где раньше был мой мозг, образовалась черная дыра небытия.

Чтобы узнать больше о моих религиозных убеждениях в отношении преобразователей значений, триггеров, мультисвязей и других наихудших практик для разработки реальных LOB - см. Следующую ветку на WPF Disciples .

Однако давайте сосредоточимся на том, чтобы вас распутать. Вам нужен - ViewModel .

Вот что вам нужно поместить в эту ViewModel:
1. Элементы управления не привязываются друг к другу. Это мерзость невиданная с библейских времен. Всякий раз, когда что-то меняется в элементе управления (ComboBox.SelectedItem, TextBox.Text и т. Д.), Связывайте это непосредственно с ViewModel. Таким образом, вместо привязки к PlaneSelector.SelectedItem и PlaneSelector.SelectedItem.VisibleElements привяжите selectedItem обратно к ViewModel.
2. Преобразование между вашими бизнес-данными и соответствующим визуальным представлением в ViewModel. Итак, пусть модель представления возвращает геометрические данные.

Вот черновик того, как выглядит супер простая модель ViewModel:

public class myFormViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void InvokePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler changed = PropertyChanged;
        if (changed != null) changed(this, new PropertyChangedEventArgs(propertyName));
    }


    private object _planeSelectedItem;
    public object PlaneSelectedItem
    {
        get { return _planeSelectedItem; }
        set
        {
            _planeSelectedItem = value;
            InvokePropertyChanged("PlaneSelectedItem");
        }
    }

    public IEnumerable<KeyValuePair<string, Geometry>> VisibleElements
    {
        get
        { 
           foreach(var slenderMember in PlaneSelectedItem.VisibleElements)
            {
                yield return new KeyValuePair<string, Geometry>(slenderMember.AsString, ToGeometry(slenderMember));
            }
        }
    }
}

И вот как примерно должна выглядеть эта часть вашего контроля:

<ComboBox SelectedItem="{Binding PlaneSelectedItem, Mode=TwoWay}" />
<ItemsControl x:Name="Display" 
      Background="White"
      HorizontalAlignment="Stretch" 
      VerticalAlignment="Stretch"
      ItemsSource="{Binding VisibleElements}">
    <ItemsControl.ItemTemplate>
        <DataTemplate >
            <Path Stroke="Blue" StrokeThickness=".5"
                  Data="{Binding Value}" ToolTip="{Binding Key}">
            </Path>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Очевидно, здесь есть некоторый пропущенный код (установка this.DataContent = new myFormViewModel (), использование KeyValuePairs, установка ComboBox ItemsSource и т. Д.). Но суть здесь в том, чтобы упростить эти сумасшедшие обязательные махинации.

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

1 голос
/ 28 сентября 2009

Вы не можете. Просто сдавайся ...

нет

Нет-нет. Подожди :). Используйте MultiBinding вместо привязки. По крайней мере, похоже, что вы пытаетесь сделать что-то, что вам позволяет.

Надеюсь, это поможет.

0 голосов
/ 17 сентября 2009

Я не очень уверен, но вы пытались использовать DynamicResource в вашем ConverterParameter, как в

        <Path Stroke="Blue" StrokeThickness=".5"
          Data='{Binding Converter={StaticResource SlenderMemberConverter}, 
                        ConverterParameter={DynamicResource ConverterParameter2}}'
          ToolTip="{Binding AsString}">
        </Path>

0 голосов
/ 17 сентября 2009

Вы всегда можете следить за ошибками привязки в окне вывода. Всегда начинается с System.Windows.DataError ...

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