透过信托完成多窗口间的传值,在程序代码中选

在使用WPF开发的时候就不免会遇到需要两个窗口间进行传值操作,当然多窗口间传值的方法有很多种,本文介绍的是使用委托实现多窗口间的传值。

简介

  本文将完整叙述我利用VisualTreeHelper实现题述功能的全部过程,想直接看函数实现的朋友可以跳到函数实现部分。 
  或者直接在GitHub上下载源码。 
   
  在WPF中我们经常会遇到这种情况:当我们尝试着去寻找窗体或者页面中某个控件的子控件或者父控件的时候,我们只能寻找到它的第一级的逻辑子级对象或者父级对象。当我们想更深入的时候,就没有办法了。 
  甚至在我们自定义的DataTemplate中的控件,我们都没办法对其访问。比如在ListView中自定义的控件,我们就没办法获取指定位置的控件了。相关例子可以参见我的博文: WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探 。 
  本文将探讨解决方案。

下面我实现一个最简单的页面传值功能,相信初学者能一看就明白。

看到博客园有篇文章谈到“使用C#编程的方式创建DataTemplate数据模板”(原文地址), 博主的做法是创建一个FrameworkElementFactory对象,设置好后将其设置为DataTemplate对象的VisualTree属 性。我认为此方法有待商榷,盖因查阅MSDN,发现FrameworkElementFactory类的介绍页面上有一段备注:
(http://msdn.microsoft.com/zh-cn/library/system.windows.frameworkelementfactory.aspx)

在上代码之前呢,先简单介绍一下什么是C#中的委托(如果只想了解如何传值可以略过这部分)在网络上有很多对于委托的介绍和讲解,经过我的学习和总结加上了一点我自己的理解,我认为委托是一种类似于C语言的指针,但是它指向的是方法而不是变量。如果把委托看作一个变量,那么这个变量里存着的就是你目标方法的地址,调用委托约等于调用你的目标方法。(个人理解欢迎指正交流)

VisualTreeHelper

  微软在VisualTreeHelper类中,提供了一些实用工具方法,用于执行涉及可视化树中的节点的常规任务,VisualTreeHelper 类中的一些方法可以接受表示任意一种可视对象类型的 DependencyObject 值。 
  这里我们将要用到两个方法分别是:VisualTreeHelper.GetChild()和VisualTreeHelper.GetParent()。

图片 1点击打开按扭,打开传输值窗体

  备注:通过此类以编程方式创建模板这种方式已被否决,这些模板是 FrameworkTemplate(如 ControlTemplate 或 DataTemplate)的子类;使用此类创建模板时,并非所有模板功能都可用。以编程方式创建模板的推荐方式是:使用 XamlReader 类的 Load 方法从字符串或内存流中加载 XAML。

 

使用VisualTreeHelper

图片 2图片 3View Code


以下正文:

模拟场景搭建

  新建一个WPF工程,命名为VisualTreeHelperDemo。 
  假设我们有如下如所示的一个主窗体,窗体的内容容器为一个name=”TopGrid”的Grid控件,它包含了上下两个子级Grid,每个子级Grid中各自包含了一个Button。 
  图片 4 
  MainWindow.xaml代码如下:  

 1 <Window x:Class="VisualTreeHelper.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="MainWindow" Height="350" Width="525">
 5     <Grid Name="TopGrid">
 6         <Grid.RowDefinitions>
 7             <RowDefinition></RowDefinition>
 8             <RowDefinition></RowDefinition>
 9         </Grid.RowDefinitions>
10         <Grid >
11             <Button Content="Button1" Name="btn_One" VerticalAlignment="Center" HorizontalAlignment="Center">
12             </Button>
13         </Grid>
14         <Grid  Grid.Row="1">
15             <Button Content="Button2" Name="btn_Two" VerticalAlignment="Center" HorizontalAlignment="Center">
16             </Button>
17         </Grid>
18     </Grid>
19 </Window>

图片 5;)

  因此,正确的做法似乎是编程创建一段XAML代码并且使用XamlReader类的Load方法将其构造成为实例对象。下面代码展示了这一做法(为了演示,数据绑定操作全部由C#代码而不是xaml完成):

实现窗口间的相互传值,先创建两个窗口,先上代码主窗口代码:

遍历寻找子级对象

  现在我们来寻找TopGrid所有Button子级对象,并输出它们的名称。 
  打开MainWindow.xaml.cs文件,添加寻找子级对象的代码如下:

 1 /// <summary>
 2 /// 利用visualtreehelper寻找对象的子级对象
 3 /// </summary>
 4 /// <typeparam name="T"></typeparam>
 5 /// <param name="obj"></param>
 6 /// <returns></returns>
 7 List<T> FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
 8 {
 9     try
10     {
11         List<T> TList = new List<T> { };
12         for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i  )
13         {
14             DependencyObject child = VisualTreeHelper.GetChild(obj, i);
15             if (child != null && child is T)
16             {
17                 TList.Add((T)child);
18                 List<T> childOfChildren = FindVisualChild<T>(child);
19                 if (childOfChildren != null)
20                 {
21                     TList.AddRange(childOfChildren);
22                 }
23             }
24             else
25             {
26                 List<T> childOfChildren = FindVisualChild<T>(child);
27                 if (childOfChildren != null)
28                 {
29                     TList.AddRange(childOfChildren);
30                 }
31             }
32         }
33         return TList;
34     }
35     catch (Exception ee)
36     {
37         MessageBox.Show(ee.Message);
38         return null;
39     }
40 }

 在btn_One_Click事件里面书写代码如下:

 

 1 /// <summary>
 2 /// 窗体加载事件
 3 /// </summary>
 4 /// <param name="sender"></param>
 5 /// <param name="e"></param>
 6 private void btn_One_Click(object sender, RoutedEventArgs e)
 7 {
 8     string btnName = "";
 9     List<Button> btnList = FindVisualChild<Button>(TopGrid);
10     foreach (Button item in btnList)
11     {
12         btnName  = string.IsNullOrEmpty(btnName) ? item.Name.ToString() : ","   item.Name.ToString();
13     }
14     Show(string.Format(TopGrid.Name.ToString() "共有{0}个Button,名称分别为{1}", btnList.Count, btnName));
15 }

 

运行程序,点击Button1,结果如下图: 

 
  图片 6 
   
  结果表明遍历成功。 

 

 1 public partial class Form1 : Form
 2     {
 3         public Form1()
 4         {
 5             InitializeComponent();
 6         }
 7 
 8         public void getValue(string strV)
 9         {
10             this.textBox1.Text = strV;
11         }
12 
13         private void button1_Click(object sender, EventArgs e)
14         {
15             Form2 frm = new Form2();
16             //frm.fatherform = this;//将当前窗体赋给fatherform
17             //frm.getTextHandler  = new Form2.GetTextHandler(getValue);//给事件赋值(注意:GetText方法的参数必须与GetTextHandler委托的参数一样,方可委托)
18             frm.getTextHandler = getValue;//将方法赋给委托对象
19             frm.ShowDialog();
20         }
21     }

  下面代码完成一个小程序,该程序可以输入名字和昵称,将之加入到ListBox列表中,如果选中ListBox某一项,输入框内容会实时更新,也可以实时修改名字和昵称。首先使用XAML描述出程序界面:

MainWindow.xaml

遍历寻找父级对象

  现在我们来遍历Button2的父级对象,获得其所有父级对象的信息,并且展示。 
  打开MainWindow.xaml.cs文件,添加寻找父级对象的代码如下:

 1 /// <summary>
 2 /// 利用VisualTreeHelper寻找指定依赖对象的父级对象
 3 /// </summary>
 4 /// <typeparam name="T"></typeparam>
 5 /// <param name="obj"></param>
 6 /// <returns></returns>
 7 public static List<T> FindVisualParent<T>(DependencyObject obj) where T : DependencyObject
 8 {
 9     try
10     {
11         List<T> TList = new List<T> { };
12         DependencyObject parent = VisualTreeHelper.GetParent(obj);
13         if (parent != null && parent is T)
14         {
15             TList.Add((T)parent);
16             List<T> parentOfParent = FindVisualParent<T>(parent);
17             if (parentOfParent !=null)
18             {
19                 TList.AddRange(parentOfParent);
20             }
21         }
22         else if ( parent != null )
23         {
24              List<T> parentOfParent = FindVisualParent<T>(parent);
25              if (parentOfParent != null)
26              {
27                  TList.AddRange(parentOfParent);
28              }
29         }
30         return TList;
31     }
32     catch (Exception ee)
33     {
34         MessageBox.Show(ee.Message);
35         return null;
36     }
37 }

在btn_Two_Click中添加代码如下:

 

 1 /// <summary>
 2 /// 遍历Button2父级对象信息
 3 /// </summary>
 4 /// <param name="sender"></param>
 5 /// <param name="e"></param>
 6 private void btn_Two_Click(object sender, RoutedEventArgs e)
 7 {
 8     string parentName = "";
 9     List<Grid> gridList = FindVisualParent<Grid>(btn_Two);
10     foreach (Grid item in gridList)
11     {
12         parentName  = string.IsNullOrEmpty(parentName) ? item.Name.ToString() : ","   item.Name.ToString();
13     }
14     MessageBox.Show(string.Format(btn_Two.Name.ToString()   "共有{0}个逻辑父级,名称分别为{1}", gridList.Count, parentName));
15 }

 

运行程序,点击Button2,效果如下: 
图片 7 
  结果表明遍历成功。

图片 8;)

MainWindow.xaml:

<Grid>
  <TextBox Name="MainWindowTextBox" HorizontalAlignment="Left" Height="23" Margin="10,61,0,0" TextWrapping="Wrap" Text="空" VerticalAlignment="Top" Width="297"/>
  <Button Content="打开新窗口" HorizontalAlignment="Left" Margin="10,130,0,0" VerticalAlignment="Top" Width="297" Click="ButtonBase_OnClick"/>
</Grid>

总结

  通过上述的方法我们就可以随心所欲地获取我们想要的控件对象,并对其进行操作,包括自定义的DataTemplate中的控件都可以获取。

 

 

图片 9

 MainWindow.xaml.cs

图片 10输入值后点击传输按扭,'value'将显示在接收值窗体的TextBox上

下面将TextBox、ListBox的数据源全部绑定到我们自己的数据结构上,先定义一个NickName类,实现INotifyPropertyChanged接口:

 1 public void GetValue(string value1, TextBox value2)
 2   {
 3     MainWindowTextBox.Text = value1;
 4   }
 5 
 6 private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
 7   {
 8     Window1 newWindow1 = new Window1();
 9     newWindow1.getTextHandler = GetValue;          //将方法赋给委托对象
10     newWindow1.ShowDialog();
11 
12   }

图片 11图片 12View Code

MainWindow.xaml.cs:

 

图片 13;)

图片 14

效果图如下:

 1 public partial class Form2 : Form
 2     {
 3         public Form2()
 4         {
 5             InitializeComponent();
 6         }
 7         //public Form1 fatherform;
 8 
 9         public delegate void GetTextHandler(string text);//声明委托
10        // public event GetTextHandler getTextHandler = null;//定义委托事件
11        public  GetTextHandler getTextHandler;//委托对象
12         private void button1_Click(object sender, EventArgs e)
13         {
14             //if (fatherform != null)
15             //{
16             //    fatherform.getValue(this.textBox1.Text.Trim());
17             //    this.Close();
18             //}
19             if (getTextHandler != null)
20             {
21                 getTextHandler(this.textBox1.Text.Trim());
22                 this.Close();
23             }
24         }
25     }

下面代码在内存中使用XAML定义了一个DataTemplate,并将之实例化,完成了ListBox的数据绑定操作:

图片 15

图片 16;)

MainWindow.xaml.cs:

第二个窗口Window1代码:

这里主要为大家呈现了两种传值方式:

图片 17

Window1.xaml

一、将Form1窗体传给fatherform对象,随后我们就可以在Form2中操作Form1了。
二、使用委托,将getValue方法赋给事件或委托对象getTextHandler,那么实现getValue操作就不用自己去做了因为已经委托给getTextHandler,直接调用getTextHandler即可

通过上述代码,已经成功使用C#代码创建了DataTemplate数据模板,并完成了这个小程序:

<Grid>
    <TextBox Name="Window1TextBox" HorizontalAlignment="Left" Height="23" Margin="84,73,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
    <Button Content="传值" HorizontalAlignment="Left" Margin="84,125,0,0" VerticalAlignment="Top" Width="120" Click="ButtonBase_OnClick"/>
</Grid>

 

图片 18

 Window1.xaml.cs

参考出处:

由于刚开始学习WPF,代码中难免有不当之处,恳请牛人指正。

1 public delegate void GetTextHandler(string value1, TextBox value2);  //声明委托
2 public GetTextHandler getTextHandler;                                //委托对象
3 
4 private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
5   {
6     getTextHandler(Window1TextBox.Text, Window1TextBox);
7   }

===================================================================

效果图:

再来看几个传值的方法,有如下几种方式,其中主要讲讲事件订阅传值的形式。

图片 19

  1. 声明个全局变量,就是App.xaml里面声明;在所有窗体里面都可以引用 Application.Current.Properties["ArgumentName"];

  2. 第二个就是 在目标窗体上面公开个 属性,直接赋值;

  3. 最后就是在Uri里面传参数 NavigationService.Navigate(window object,argument value)

  4. 采用事件响应,传递值。 

实现效果当运行程序后,点击打开新窗口按钮后,会打开Window1窗口,在Window1窗口的Textbox中输入内容,点击传值,你所输入的内容就会传到主窗口,通过委托的事件将主窗口中的Textbox控件的内容更改为你传过去的值。效果如下:

 

图片 20

举例:点击主窗口MainWindow 上的一个OpenSubWindow按钮 -> 打开子窗口SubWindow -> 在子窗口中的TextBox中输入值, 点击OK后关闭 -> 主窗口上的TextBox获取子窗口中的值。

图片 21

  1. 在子窗口中定义一个事件PassValuesEvent。 当点击 OK 按钮时,触发事件,并传递数值。( PassValuesEventArgs 是EventArgs类,需要同时定义好) 

现在就已经实现了窗口间传值的操作了。接下来我会简单介绍一下以上代码的实现方法和一些自己的理解,如果不感兴趣或者已经会使用委托进行多窗口间的传值了,后面的部分可以略过。 

图片 22;)

前台代码在此就先不介绍了哈,在MainWindow.xaml.cs文件中

    public partial class SubWindow : Window
    {
        public delegate void PassValuesHandler(object sender, PassValuesEventArgs e);

        public event PassValuesHandler PassValuesEvent; 

        public SubWindow()
        {
            InitializeComponent();
        }

        private void btnOK_Click(object sender, RoutedEventArgs e)
        {
            string value1 = tbValue1.Text;   // Text Property return value is string type .
            int value2;
            Int32.TryParse(tbValue2.Text, out value2);

            PassValuesEventArgs args = new PassValuesEventArgs(value1, value2);
            PassValuesEvent(this, args);

            this.Close();
        }
    }
public void GetValue(string value1, TextBox value2)

图片 23;)

此方法即为委托的目标方法,此方法返回值为空,也可以设置其返回值,当使用委托时也会收到目标方法的返回值。再有就是此方法接收两个参数,一个是字符串一个是TextBox,第二个参数倒是没什么实际含义,只是为了说明这里传递的变量可以多个,也可以是其它object类型。

2.  在主窗口中的OpenSubWindow按钮点击的方法中,订阅了PassValuesEvent事件。当事件触发时,获取传递的参数的值。

 

图片 24;)

newWindow1.getTextHandler = GetValue;          //将方法赋给委托对象
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnOpenSubWindow_Click(object sender, RoutedEventArgs e)
        {
            SubWindow subWindow = new SubWindow(); 

            // 订阅事件
            subWindow.PassValuesEvent  = new SubWindow.PassValuesHandler(ReceiveValues);

            subWindow.Show(); 
        }

        private void ReceiveValues(object sender, PassValuesEventArgs e)
        {
            this.tbValue1.Text = e.Value1;
            this.tbValue2.Text = e.Value2.ToString(); 
        }
    }

将方法赋给委托对象,可以理解为把他们两个绑定在一起的getTextHandler这个委托对应的目标方法就是GetValue。

图片 25;)

在Windo1.xaml.cs中:

 主要说明:子窗口的PassValuesEvent事件,是从PassValuesHandler代理的一个实例对象并且被定义成event类型,所以在其他类里就可以订阅这个事件了。

public delegate void GetTextHandler(string value1, TextBox value2);  //声明委托
public GetTextHandler getTextHandler;                                //委托对象

 

delegate是声明委托的关键字,这里的返回值为空,若目标方法是有返回值的,在这里将返回值写成同种类型即可,接收的两个变量类型也要和目标方法一致。

参考出处:

接下来就是定义委托对象,大写的GetTextHandler是委托,而小写的getTextHandler是对象,在使用该委托时候使用的也是小写的getTextHandler使用方法:

getTextHandler(Window1TextBox.Text, Window1TextBox);

 

 

行文至尾,委托的使用还有很多,例如:事件订阅,匿名方法,多播委托等等,本文介绍的则是委托的基本使用方法,其他的用法仍在学习当中,欢迎指正交流。

 

2018.8.30下午三点差五分

本文由星彩网app下载发布于计算机编程,转载请注明出处:透过信托完成多窗口间的传值,在程序代码中选

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。