用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的所有大写都改为小写,就可以完美显示了,而且还不会报错,有点-_-!。



posted @ 2011-11-28 11:26  通通的成长日记  阅读(3048)  评论(2编辑  收藏  举报