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



  • 相关阅读:
    以结构体为键值的map
    Luogu P1251 餐巾计划问题 (最小费用最大流、拆点)
    CodeForces
    CodeForces
    中缀表达式转后转表达式
    CodeForces
    Educational Codeforces Round 100
    2020年12月杂题选做
    2020年11月杂题选做
    CF542E Playing on Graph
  • 原文地址:https://www.cnblogs.com/tong-tong/p/2250948.html
Copyright © 2011-2022 走看看