如何将引用传递给另一个控件作为IValueConverter参数? [英] How can I pass a reference to another control as an IValueConverter parameter?

查看:103
本文介绍了如何将引用传递给另一个控件作为IValueConverter参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我将一些业务对象绑定到WPF ItemsControl。它们使用用于在DataTemplate中为Path对象生成几何的自定义IValueConverter实现来显示,如下所示:

  ItemsControl x:Name =Display
Background =White
Horizo​​ntalAlignment =Stretch
VerticalAlignment =Stretch
ItemsSource ={Binding ElementName = ViewPlaneSelector,
Path = SelectedItem.VisibleElements}
>
< ItemsControl.Resources>
<! - 这个对象只是用来解决ConverterParameter
不能直接绑定的事实(它不是DependencyObject上的DependencyProperty - >
< this:GeometryConverterData
x:Key =ConverterParameter2
Plane ={Binding ElementName = ViewPlaneSelector,
Path = SelectedItem}/>
< DataTemplate DataType ={x :Type:SlenderMember
< Path Stroke =BlueStrokeThickness =。5
Data ='{Binding Converter = {StaticResource SlenderMemberConverter},
ConverterParameter = StaticResource ConverterParameter2}}'
ToolTip ={Binding AsString}>
< / Path>
< / DataTemplate>
< /ItemsControl.Resources>
< / ItemsControl>

请注意,ItemsControl的项目是从ViewPlaneSelector(ComboBox)SelectedItem.VisibleElements属性。我需要在SlenderMemberConverter中使用相同的ViewPlaneSelector.SelectedItem来找出如何显示这个元素。我试图通过在资源部分中创建中间的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函数调试,看看这个属性被绑定了。它只有并且总是设置为null。我知道ViewPlaneSelector.SelectedItem并不总是null,因为ItemsControl有项目,它的项目是从相同的对象上的同一属性绘制的,所以给出了什么?我如何可以将这个ComboBox引用到我的ValueConverter中。



或者,为什么我在做什么傻,过于复杂。我有罪是因为许多人有时会把它弄入我的头脑,某些事情必须以某种方式完成,然后当有一个更清洁和更简单的解决方案时,才能使自己发生。

解决方案

对不起,你做错了。



你的整个论坛帖子是一个代码之后的味道。

1.您正在使用Value Converters将商业实体转换为视觉对应物。这是我书中的一个代码气味。

2.您正在寻找将UI控件发送到业务逻辑。

3.对上帝而言,有人在这里说MultiBindings(而不是正确的)我的头被打破了,现在有一个黑洞,我的大脑曾经是虚无的。



更多关于我对价值转换器,触发器,MultiBindings等真实世界LOB开发的最恶劣做法的宗教信念 - 请随时阅读 WPF门徒



但是,让我们专注于让你解开。
您需要一个 - ViewModel



这是您需要放入ViewModel中的内容:

1.控件不会彼此绑定。自圣经以来,这是可憎的事情。每当控件上的某个事件发生变化(ComboBox.SelectedItem,TextBox.Text,无论如何)直接绑定到ViewModel。所以,而不是绑定到PlaneSelector.SelectedItem和PlaneSelector.SelectedItem.VisibleElements,将selectedItem绑定回ViewModel。

2.在您的业务数据和ViewModel中相应的视觉表示之间进行转换。所以,让视图模型返回几何数据。



这是一个粗略的草稿,一个超级简单的ViewModel如何看起来像:

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


私有对象_planeSelectedItem;
public object PlaneSelectedItem
{
get {return _planeSelectedItem; }
set
{
_planeSelectedItem = value;
InvokePropertyChanged(PlaneSelectedItem);
}
}

public IEnumerable< KeyValuePair< string,Geometry>> VisibleElements
{
get
{
foreach(PlaneSelectedItem.VisibleElements中的var slenderMember)
{
yield返回新的KeyValuePair< string,Geometry>(slenderMember。 AsString,ToGeometry(slenderMember));
}
}
}
}

这个大概是你的控件的这个部分应该如下所示:

 < ComboBox SelectedItem ={Binding PlaneSelectedItem,Mode = TwoWay} /> 
< ItemsControl x:Name =显示
背景=白
Horizo​​ntalAlignment =拉伸
VerticalAlignment =Stretch
ItemsSource ={Binding VisibleElements}>
< ItemsControl.ItemTemplate>
< DataTemplate>
< Path Stroke =BlueStrokeThickness =。5
Data ={Binding Value}ToolTip ={Binding Key}>
< / Path>
< / DataTemplate>
< /ItemsControl.ItemTemplate>
< / ItemsControl>

显然,这里有一些缺少的代码(设置this.DataContent = new myFormViewModel(),使用KeyValuePairs ,设置ComboBox ItemsSource等)。但这里的核心是简化这些疯狂的绑定shenanigans。



而不是试图破解像XAML这样的代码,只需使用代码即可。
这将使你的生活更简单。


I am binding some business objects to a WPF ItemsControl. They are displayed using a custom IValueConverter implementation used to produce the Geometry for a Path object in the DataTemplate as shown here:

<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>

Note that the items for the ItemsControl are drawn from the ViewPlaneSelector (a ComboBox) SelectedItem.VisibleElements property. I need that same ViewPlaneSelector.SelectedItem in the SlenderMemberConverter to figure out how to display this element. I'm trying to get a reference to it into the converter by creating the intermediate GeometryConverterData object in the Resources section. This object exists solely to get around the problem of not being able to bind directly to the ConverterParameter property (as mentioned in the comments). Here is the code for the GeometryDataConverter class:

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);
        }
    }
}

I added the ValidValue function for debugging, to see what this property was getting bound it. It only and always gets set to null. I know that the ViewPlaneSelector.SelectedItem isn't always null since the ItemsControl has items, and it's items are drawn from the same property on the same object... so what gives? How can I get a reference to this ComboBox into my ValueConverter.

Or, alternately, why is what I'm doing silly and overly complicated. I'm as guilty as many of sometimes getting it into my head that something has to be done a certain way and then killing myself to make it happen when there's a much cleaner and simpler solution.

解决方案

Sorry, but you're doing it wrong.

Your entire forum post is one code smell after another.
1. You're using Value Converters to convert a business entity to it's visual counterpart. That's a code smell in my book.
2. You're looking into sending a UI control to business logic.
3. Honest to god, someone here said "MultiBindings" (and beyond being right) my head imploded and there is now a black hole of nothingness where my brain used to be.

For more on my religious convictions regarding Value Converters, Trigger, MultiBindings and other worst practices for real-world LOB development - feel free to read the following thread on WPF Disciples.

However, Let's focus on getting you untangled. You need a - ViewModel.

Here's what you need to put inside that ViewModel:
1. Controls don't bind to each other. It's an abomination unseen since biblical times. Whenever somthing on a control changes (ComboBox.SelectedItem, TextBox.Text, whatever) bind that directly to a ViewModel. So, instead of binding to PlaneSelector.SelectedItem and PlaneSelector.SelectedItem.VisibleElements, bind the selectedItem back to the ViewModel.
2. Convert between your business data and your correponding visual representation in the ViewModel. So, have the view model return the geometric data.

Here's a rough draft of how a super simple ViewModel that does that look like:

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));
            }
        }
    }
}

And here's roughly how that part of your control should look like:

<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>

Obviously, there's some missing code here (Setting the this.DataContent = new myFormViewModel(), using KeyValuePairs, setting the ComboBox ItemsSource, etc). But the core here is to simplify these crazy binding shenanigans.

Instead of trying to hack togather XAML to behave like code - just use code. It'll make your life much simpler.

这篇关于如何将引用传递给另一个控件作为IValueConverter参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆