【WPF】CommandParameter解决多传参问题

简介: 原文:【WPF】CommandParameter解决多传参问题 方法一:传参按钮控件自身绑定的ItemSource 用WAF框架实现MVVM,按钮的点击事件都要通过Command来传递到这个View对应的ViewModel上,再通过ViewModel传递到上层的Controller层,在Controller层通过DelegateCommand处理按钮真正的事件。
原文: 【WPF】CommandParameter解决多传参问题

方法一:传参按钮控件自身绑定的ItemSource

用WAF框架实现MVVM,按钮的点击事件都要通过Command来传递到这个View对应的ViewModel上,再通过ViewModel传递到上层的Controller层,在Controller层通过DelegateCommand处理按钮真正的事件。有时候需要给该Command附加上一些参数(CommandParameter),但是默认CommandParameter只能传递一个参数。谷歌搜到的解决方法很复杂,于是想了个办法CommandParameter参数传递的是这个按钮控件自身绑定的ItemSource,然后通过从ItemSource身上的DataContext来拿到数据,再截取字符串分割得到想要的部分数据(或者强转为ItemSource对应的实体类)。

正常情况下,Button的绑定:

<Button Command="{Binding BtnCommand}" />

这个Command会沿着View –> ViewModle –> Controller层传递。

如果这个Button是ListBox的Item,这个ListBox的Item要使用数据模板,且ItemsSource绑定到了这组Button的数据源,是这样绑:

<ListBox x:Name="listBox" ItemsSource="{Binding DataList}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid x:Name="grid">
                <local:WaitingProgress/>
                <Button Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.BtnCommand}"
                    CommandParameter="{Binding RelativeSource={x:Static RelativeSource.Self}}"> <!-- 传参是Button控件绑定的ItemsSource,即这里是DataList列表 -->
                    <Image Tag="{Binding id}" x:Name="img" Stretch="UniformToFill" Width="150" Height="150" webImg:ImageDecoder.Source="{Binding icon}">
                    </Image>
                </Button>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>

    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel Name="wrapPanel" HorizontalAlignment="Stretch" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

这个ItemSource绑定的DataList是ViewModle中的一个实例列表。ViewModel关键代码如下:

private ICommand refreshDesignCommand; // 向上传递这个Command:View-->ViewModel-->Controller
public ICommand RefreshDesignCommand
{
    get { return refreshDesignCommand; }
    set { SetProperty(ref refreshDesignCommand, value); }
}

private ObservableCollection<GoodsJsonData> dataList = null;
public ObservableCollection<GoodsJsonData> DataList
{
    get { return dataList; }
    set { dataList = value; }
}

实体类:

public class GoodsJsonData
{
    public string id { get; set; }      // 还可用于图片被点击调时,标记出是哪个缩略图被点击
    public string icon { get; set; }    // 缩略图
    public string image { get; set; }   // 大图
    public string model { get; set; }   // 该商品对应的模型XML

    public override string ToString()
    {
        return "id = " + id + " , icon = " + icon + " , image = " + image + " , model = " + model;
    }
}

Controller层的关键代码:

private readonly DelegateCommand refreshDesignCommand;  // 缩略图的点击回调

[ImportingConstructor]
public WebImageController()
{
    this.refreshDesignCommand = new DelegateCommand(p => RefreshDesignCommand((Button)p));
}

private void RefreshDesignCommand(Button btn)
{
    // 方法一:将DataContext打印字符串,截取出目标数据
    string dataContext = btn.DataContext.ToString();
    System.Console.WriteLine(dataContext);          // id = 000201 , icon = http://192.168.1.222/mjl/4-01.png , image = 2/造型/4-01.png , model = xml/qiang07.xml
    // 截取字符串来获得目标数据。

    // 方法二:将DataContext强转为ItemSource对应的实体类类型
    GoodsJsonData data = (GoodsJsonData)btn.DataContext;
    // do what you want !

坑点:

  • 如果这个DataList列表的内容需要同步刷新,则类型**必须是**ObservableCollection。否则就算控件与数据绑定成功,控件只在初始化时能够正确显示数据,之后数据发生改变时,控件不会自动刷新。
  • WPF可以传递控件自身绑定的ItemSource数据,通过ItemSource携带的DataContext内容,来代替CommandParameter多传参的蛋疼问题。

其他建议:

  • 想要在一个控件上传递多个参数,可以传递控件自身,用控件的Tag和Uid属性绑定上数据。

 

今天在StackOverflow看到一个关于解决Command和CommandParameter的工具:
http://xcommand.codeplex.com/
以后可能会用到,先Mark。之后抽空看看。


 

方法二:多路绑定MultiBinding结合转换器Converter的使用

该方法是网上搜到的主流方式。

 


 

方法三:其他Trick

思路:用其他控件的属性来记录数据。传参时传递按钮控件自身,再通过按钮控件的视觉树布局找到找到绑定了其他数据的控件。

XAML:

<Grid>
    <Button Content="测试按钮"
        Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.YourCommand}"
        CommandParameter="{Binding RelativeSource={x:Static RelativeSource.Self}}">
    </Button>
    <!-- 用于给点击按钮时,传递多个参数 -->
    <Grid x:Name="studentIdGrid" Tag="{Binding studentId}"/>
    <Grid x:Name="studentNameGrid" Tag="{Binding studentName}"/>
    <Grid x:Name="studentAgeGrid" Tag="{Binding studentAge}"/>
</Grid>

Controller:

// 按钮点击触发的事件
private void YourCommand(object p)
{
    Button btn = (Button)p; // 传参传递的是控件自身
    DependencyObject parent = VisualTreeHelper.GetParent(btn);
    List<Grid> list = this.FindVisualChildren<Grid>(parent);
    string studentId = list[0].Tag.ToString();
    string studentName = (int)(list[1].Tag);
    int studentAge = list[2].Tag.ToString();
    
    // do something...
}


// 从视觉树找到目标控件的所有子控件
private List<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
    List<T> list = new List<T>();
    if (depObj != null)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
            if (child != null && child is T)
            {
                list.Add((T)child);
            }

            List<T> childItems = FindVisualChildren<T>(child); // 递归
            if (childItems != null && childItems.Count() > 0)
            {
                foreach (var item in childItems)
                {
                    list.Add(item);
                }
            }
        }
    }
    return list;
}

小结:

  • 按钮的CommandParameter绑定了自身,将自身作为点击后触发的回调函数的参数传入。再用该按钮控件去找到其他控件或UI元素。
  • 使用了按钮的兄弟节点Grid的Tag属性来绑定目标数据,选择用Grid是因为我们只想用它来传参,而不需要看到它。因此用其他UI元素的Tag属性来传参,再设置Visibility="Collapse"也是可行的。
  • 同样选择用兄弟节点也不是必须的。也可以是父节点或子节点,只要能通过视觉树VisualTreeHelper找到就行。

 

目录
相关文章
|
前端开发 C#
WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参
原文:WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参 ContextMenu无论定义在.cs或.xaml文件中,都不继承父级的DataContext,所以如果要绑定父级的DataContext,直接DataContext=“{Bind...
3277 0
|
1月前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
108 0
|
1月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
80 1
|
10月前
|
C# Windows
WPF技术之图形系列Polygon控件
WPF Polygon是Windows Presentation Foundation (WPF)框架中的一个标记元素,用于绘制多边形形状。它可以通过设置多个点的坐标来定义多边形的形状,可以绘制任意复杂度的多边形。
556 0
|
10月前
|
C# Windows
WPF技术之RichTextBox控件
WPF RichTextBox是Windows Presentation Foundation (WPF)中提供的一个强大的文本编辑控件,它可以显示富文本格式的文本,支持多种文本处理操作。
382 0
|
1月前
|
前端开发 C# 容器
浅谈WPF之控件拖拽与拖动
使用过office的visio软件画图的小伙伴都知道,画图软件分为两部分,左侧图形库,存放各种图标,右侧是一个画布,将左侧图形库的图标控件拖拽到右侧画布,就会生成一个新的控件,并且可以自由拖动。那如何在WPF程序中,实现类似的功能呢?今天就以一个简单的小例子,简述如何在WPF中实现控件的拖拽和拖动,仅供学习分享使用,如有不足之处,还请指正。
132 2
|
10月前
|
数据挖掘 数据处理 C#
WPF技术之DataGrid控件
WPF DataGrid是一种可以显示和编辑数据的界面控件。它可以作为表格形式展示数据,支持添加、删除、修改、排序和分组操作。
207 0
|
1月前
|
C# 开发者 C++
一套开源、强大且美观的WPF UI控件库
一套开源、强大且美观的WPF UI控件库
180 0
|
7月前
|
算法 C# UED
浅谈WPF之控件模板和数据模板
WPF不仅支持传统的Windows Forms编程的用户界面和用户体验设计,同时还推出了以模板为核心的新一代设计理念。在WPF中,通过引入模板,将数据和算法的“内容”和“形式”进行解耦。模板主要分为两大类:数据模板【Data Template】和控件模板【Control Template】。
120 8
|
10月前
|
定位技术 C# UED
WPF技术之ScrollViewer控件
WPF ScrollViewer是WPF中常用的一个控件,它提供了滚动视图的功能,可用于显示超出容器可视区域的内容。ScrollViewer通常用于容纳大量内容的控件,以在有限的空间内显示这些内容,并允许用户通过滚动来查看隐藏的部分。
912 0