用MVVM模式开发中遇到的零散问题总结(1)

简介: 原文:用MVVM模式开发中遇到的零散问题总结(1)关看这个标题略显业余,其中的任何一个问题都是困扰我几个小时才找到答案的,以供以后温故而知新,希望也能帮助到你 本节碰到的问题如下:    1.ViewModel动态切换内容XAML   2.
原文: 用MVVM模式开发中遇到的零散问题总结(1)


关看这个标题略显业余,其中的任何一个问题都是困扰我几个小时才找到答案的,以供以后温故而知新,希望也能帮助到你

本节碰到的问题如下: 

 

1.ViewModel动态切换内容XAML

 

2.将一个字符串作为Xaml的resources供ViewModel调用

 

3.将数据双向绑定到dictionary其中的一项上

 

4.通过可视化树来修改动态创建的UserControl的模版(Template)内容

 

5.让任何控件都可以绑定Command

 

6.使用相对路径访问Application不认识的文件 

 


 

1.ViewModel动态切换内容XAML


string url = "view/V" + Convert.ToString(i + 1) + "/V" + Convert.ToString(i + 1)+"1.xaml";//生成地址
FileStream fs = new FileStream(url, FileMode.Open, FileAccess.Read);//动态加载XAML
UserControl uc = XamlReader.Load(fs) as UserControl;
myContentControl.Content = uc;

其实就是先动态读入XAML文件流,再反序列化,生成的实力赋到容器中去,这里我的XAML是以usercontrol为根节点的。

注意:在写ViewModel的命名空间时一定要加上assembly,如:

xmlns:my="clr-namespace:CopSurface;assembly=CopSurface"   

 

 

 

  2.将一个字符串作为Xaml的resources供ViewModel调用


命名空间:xmlns:sys="clr-namespace:System;assembly=mscorlib"

然后 <sys:String x:Key="title">啦啦啦</sys:String>   添加进资源

后台: stateButton.Content = uc.TryFindResource("title") as string;

看图可见,基本所有类型都有了 

 

 

 

 3.将数据双向绑定到dictionary其中的一项上


 定义:

public Dictionary<string, object> data//数据字典
{
get;
set;
}

赋值并更新界面:

model.data["inputText"] += str;
model.OnPropertyChanged("data");

XAML:

<TextBox Text="{Binding data[inputText],Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>

  其中DataContent、OnPropertyChanged这些你们懂的,不懂的去看看MVVM基础。

  这个问题困扰了我一个星期,就这么简单?是这么简单,只是我的问题出在别处,我尝试了CollectionObvious和MR.WPF的dictionaryObvious类都没有效果,只能实现单项绑定,dictionary的更新总是通知不到界面...

  原来问题是由于我是新建了一个线程创建来窗口,必须要获取该窗口的Dispatcher,然后用它来执行OnPropertyChanged(),就可以更新了,真的是基础不牢靠的后果啊...

win.win.Dispatcher.Invoke(new Action(() =>
{
                     if (!model.data.ContainsKey(nameStr))//如果字典中不存在则创建
                        {
                            model.data.Add(nameStr, "");
                            model.data[nameStr] += str;
                            model.OnPropertyChanged("data");
                        }
                        else
                        {
                            int index=focusTextBox.CaretIndex;//获取光标位置
                            focusTextBox.Text = focusTextBox.Text.Insert(index,str);//在光标位置插入字符串
                            focusTextBox.SelectionStart = index+1;//光标位置后移1位
                        }
                }));

   细心的博友已经发现为什么我要绑定到dictionary而不直接绑定到属性呢?优势不言而喻,当我的View为一张银行用户开户单时那个数据啊~~是不可能设置这么多属性来一一对应的,这样当View层的Command命令执行过来时先判断dictionary中是否已经存在该项,没有就添加,这样通用性就很强了。

 

 

 

 4.通过可视化树来修改动态创建的UserControl的模版(Template)内容


  通常我们需要修改Template模版内的具体属性时通过VisualTreeHelper这个类来实现,一般情况下(为了突出重点,我把代码精简了点):

<ListBox x:Name="listBox" Style="{DynamicResource ListBoxStyle1}"/>

然后模版:

<Style x:Key="ListBoxStyle1" TargetType="{x:Type ListBox}">
。。。。。。
<ControlTemplate TargetType="{x:Type ListBox}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
<ScrollViewer x:Name="myScrollViewer" Focusable="false" Padding="{TemplateBinding Padding}" Template="{DynamicResource ScrollViewerControlTemplate1}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
。。。。。。。。。。。。。
</ControlTemplate>
</Style>

后台:

        Border myBorder = VisualTreeHelper.GetChild(listBox, 0) as Border;      
            ScrollViewer myScrollViewer = myBorder.FindName("myScrollViewer") as ScrollViewer;
return myScrollViewer;

这样就获取到ListBox中的myScrollViewer控件了,从而就可以控制滚动条的滑动。

 

而..如果我ListBox是后台动态生成的呢?比如,我需要动态生成一堆相同样子的控件,模版我定义在资源里

模版资源

 <ControlTemplate x:Key="menuStyle">
<Grid Width="80" Height="80">
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFA0A5E7" Offset="1"/>
<GradientStop Color="#FFEDEDEF"/>
</LinearGradientBrush>
</Grid.Background>
<Label x:Name="title" Margin="0,31.5,0,19"/>
<Label Name="state" Height="20.837" VerticalAlignment="Top" HorizontalAlignment="Right" Width="20.457"/>
</Grid>
</ControlTemplate>

后台生成:

                   UserControl menu = new UserControl();                                
                                menu.Template = TryFindResource("menuStyle") as ControlTemplate;//把模版资源付过去                                
                                buttonList.Items.Add(menu);

                  Grid myBorder = VisualTreeHelper.GetChild(sender as FrameworkElement, 0) as Grid;
                  Label myLabel = (myBorder.FindName("title") as Label);                  
                  myLabel.Content = "OK";

  这个时候在执行VisualTreeHelper.GetChild()方法时就会报错“超出索引...”,为什么呢?很简单....因为可视树还没有生成,当然获取不到子元素咯....和我一样基础不牢靠的赶紧补基础...

正确答案为:

menu.Template = TryFindResource("menuStyle") as ControlTemplate;//把模版资源付过去
menu.Loaded+=new RoutedEventHandler(menu_Loaded);//当加载完毕后才能查找可视化树
buttonList.Items.Add(menu);

 

private void menu_Loaded(object sender, RoutedEventArgs e)
{
Grid myBorder = VisualTreeHelper.GetChild(sender as FrameworkElement, 0) as Grid;
Label myLabel = (myBorder.FindName("title") as Label);
myLabel.Content = "OK";
}

如此简单...确纠结了我一下午....基础啊..

 

 

 

 5.让任何控件都可以绑定Command


 

很多人看到这个标题的时候首先就联想到如下代码:

<Grid Margin="0" Width="Auto" Background="#00000000" >   		
  		<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding changeContent}" CommandParameter="{Binding XPath=content}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>


问题的关键不在于此,当在Blend中直接拖动行为InvokeCommandAction到控件上就会直接加上上述代码,并且添加命名空间

但是当我们在VS2010中直接写代码来实现的时候就不能用这个命名空间,不然报错找不到Interaction.Triggers要用下面这个命名空间

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

并添加程序集引用:System.Windows.Interactivity.dll
(上面的XAML有一个小技巧就是当Grid无背景色时是响应不了鼠标事件的,但是给它添加一个透明度为0的颜色为背景色就可以触发了,但是当我把它用在listBox的itemTemplate里面的时候,又触发不了了,求解释) 

 

 

 

 6.使用相对路径访问Application不认识的文件


 

所谓不认识的文件就是文件不是在项目中添加的,而是直接Copy到exe文件目录里面。

 

<Grid.Background>
<ImageBrush ImageSource="pack://SiteOfOrigin:,,,/View/menuImg/bg.jpg"/>
</Grid.Background>

 

当然了,.exe实在Bin目录下。和网页的区别就是并不是相对于XAML的目录。

这样使用在Blend里会报错

不用管它,直接编译就行。

更新:在Blend里把"pack://SiteOfOrigin:,,,/View/menuImg/bg.jpg"改为"pack://siteoforigin:,,,/View/menuImg/bg.jpg"就是把SiteOfOrigin的所有大写都改为小写,就可以完美显示了,而且还不会报错,有点-_-!。



目录
相关文章
|
1月前
|
算法 测试技术
模块化设计具体应该怎么做呢
【10月更文挑战第22天】模块化设计具体应该怎么做呢
|
4月前
|
前端开发 C# 开发者
WPF开发者必读:MVVM模式实战,轻松构建可维护的应用程序,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离关注点,提高了代码的可维护性和可扩展性。本文详细介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定与逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种模式,开发者可以更高效地构建桌面应用程序。希望本文能帮助你在WPF开发中更好地应用MVVM模式。
227 1
|
4月前
|
前端开发 Java UED
JSF 面向组件开发究竟藏着何种奥秘?带你探寻可复用 UI 组件设计的神秘之路
【8月更文挑战第31天】在现代软件开发中,高效与可维护性至关重要。JavaServer Faces(JSF)框架通过其面向组件的开发模式,提供了构建复杂用户界面的强大工具,特别适用于设计可复用的 UI 组件。通过合理设计组件的功能与外观,可以显著提高开发效率并降低维护成本。本文以一个具体的 `MessageComponent` 示例展示了如何创建可复用的 JSF 组件,并介绍了如何在 JSF 页面中使用这些组件。结合其他技术如 PrimeFaces 和 Bootstrap,可以进一步丰富组件库,提升用户体验。
62 0
|
7月前
|
移动开发 前端开发 数据管理
构建高效Android应用:采用MVVM架构与LiveData的全面指南
在移动开发领域,构建一个既快速又可靠的应用对于开发者来说至关重要。随着Android Jetpack组件的推出,MVVM(Model-View-ViewModel)架构和LiveData已成为实现响应式、可测试且易于维护应用的首选解决方案。本文将深入探讨如何在Android应用中实施MVVM模式,以及如何利用LiveData来优化UI组件的数据更新流程,确保用户界面与业务逻辑之间的高度解耦和流畅交互。
142 4
|
7月前
|
设计模式 前端开发 测试技术
什么是mvvm模式,优点是什么
什么是mvvm模式,优点是什么
198 0
|
Java 关系型数据库 程序员
【组件设计开发】采用领域驱动设计设计和开发可组装的组件
采用领域驱动设计设计和开发可组装的组件
27953 7
【组件设计开发】采用领域驱动设计设计和开发可组装的组件
|
消息中间件 JavaScript 小程序
架构设计:为什么说复用是邪恶的?
架构设计:为什么说复用是邪恶的?
|
数据可视化 开发工具 开发者
|
前端开发 Windows
用MVVM模式开发中遇到的零散问题总结(2)
原文:用MVVM模式开发中遇到的零散问题总结(2) 本节目录:  1.解决动画属性被劫持问题 2.设置页面焦点默认所在对象 3.XAML模拟键盘按键 4.DataGrid数据源绑定到复杂格式(dynamic类的运用)   本系列文章快速导航: 用MVVM模式开发中遇到的零散问题总结(1)     1.
972 0
下一篇
DataWorks