2015-10-18 322 views
2

我做了一个页面和一个UserControl Pager。两者都有他们的ViewModel。在Pager中,存在三个依赖项属性Rows,Columns,Source。我想将这些属性从Pager的视图传递给Pager的ViewModel。我在View的代码背后尝试了这一点。但它不起作用... PagerViewModel中的set属性在调试时不会被调用。请帮我...如何将View中的自定义属性绑定到ViewModel上?

下面是详细的机制:

MainPageViewModel

↓通与绑定值

MainPage

↓集THT其值从MainPagerViewModel

性质

Pager(代码后面)

↓将属性绑定到PagerViewModel < ---这部分是问题!!!

PagerViewModel

↓通用结合

Pager(XAML)的值

,这里是源

[MainPageViewModel.cs]

using System.Collections; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Linq; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Media; 
using Client.Model; 

namespace Client.ViewModel 
{ 
    public class MainPageViewModel : ViewModelBase 
    { 
     ... 
     public ObservableCollection<IPagableEntry> PagerTableCategoriesItems { get { return TableCategoryRepository.Instance.TableCategories; } } 

     public int PagerTableCategoriesRows { get { return 1; } } 

     public int PagerTableCategoriesColumns { get { return 3; } } 
     ... 
    } 
} 

[的MainPage。 xaml]

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:view="clr-namespace:Client.View" 
     xmlns:viewModel="clr-namespace:Client.ViewModel" 
     xmlns:resStr="clr-namespace:Client.CommonResources.String" 
     x:Class="Client.View.MainPage" 
     Style="{StaticResource common}"> 
    <Page.DataContext> 
     <viewModel:MainPageViewModel /> 
    </Page.DataContext> 
    ... 

    <view:Pager x:Name="pagerTableCategories" 
       Source="{Binding Path=PagerTableCategoriesItems}" 
       Rows="{Binding Path=PagerTableCategoriesRows}" 
       Columns="{Binding Path=PagerTableCategoriesColumns}"> 
    </view:Pager> 
    ... 
</Page> 

[Pager.xaml.cs]

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Linq; 
using System.Runtime.CompilerServices; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Controls.Primitives; 
using System.Windows.Data; 
using System.Windows.Input; 
using Client.Model; 
using Client.ViewModel; 

namespace Client.View 
{ 
    public partial class Pager 
    { 

     public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(ObservableCollection<IPagableEntry>), typeof(Pager), new PropertyMetadata(null, OnSourceChanged)); 
     public static readonly DependencyProperty RowsProperty = DependencyProperty.Register("Rows", typeof(int), typeof(Pager), new PropertyMetadata(1, OnRowsChanged)); 
     public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(int), typeof(Pager), new PropertyMetadata(1, OnColumnsChanged)); 
     public static readonly DependencyProperty SelectedEntryProperty = DependencyProperty.Register("SelectedEntry", typeof(object), typeof(Pager), new PropertyMetadata(null, OnSelectedEntryChanged)); 

     public int Rows 
     { 
      get { return (int)GetValue(RowsProperty); } 
      set { SetValue(RowsProperty, value); } 
     } 

     public int Columns 
     { 
      get { return (int)GetValue(ColumnsProperty); } 
      set { SetValue(ColumnsProperty, value); } 
     } 

     public object SelectedEntry 
     { 
      get { return GetValue(SelectedEntryProperty); } 
      set { SetValue(SelectedEntryProperty, value); } 

     } 

     public ObservableCollection<IPagableEntry> Source 
     { 
      get { return (ObservableCollection<IPagableEntry>)GetValue(SourceProperty); } 
      set { SetValue(SourceProperty, value); } 
     } 

     public Pager() 
     { 
      InitializeComponent(); 

      // I want to bind the three custom properties(Rows, Columns, Source) to PagerViewModel's Rows, Columns, Collection 
      Binding bindingRows = new Binding("Rows"); 
      bindingRows.Mode = BindingMode.TwoWay; 
      bindingRows.Source = gridPager.DataContext; 
      gridPager.SetBinding(RowsProperty, bindingRows); 

      Binding bindingColumns = new Binding("Columns"); 
      bindingColumns.Mode = BindingMode.TwoWay; 
      bindingColumns.Source = gridPager.DataContext; 
      gridPager.SetBinding(ColumnsProperty, bindingColumns); 

      Binding bindingSource = new Binding("Collection"); 
      bindingSource.Mode = BindingMode.TwoWay; 
      bindingSource.Source = gridPager.DataContext; 
      gridPager.SetBinding(SourceProperty, bindingSource); 
     } 

     private void ListBoxEntriesOnSelectionChanged(object sender, SelectionChangedEventArgs e) 
     { 
      SelectedEntry = (sender as ListBox).SelectedItem; 
     } 

     private static void OnSelectedEntryChanged(DependencyObject pager, DependencyPropertyChangedEventArgs e) 
     { 
      (pager as Pager).SelectedEntry = e.NewValue; 
     } 

     private static void OnSourceChanged(DependencyObject pager, DependencyPropertyChangedEventArgs e) 
     { 
      (pager as Pager).Source = (ObservableCollection<IPagableEntry>)e.NewValue; 
     } 

     private static void OnRowsChanged(DependencyObject pager, DependencyPropertyChangedEventArgs e) 
     { 
      (pager as Pager).Rows = (int)e.NewValue; 
     } 

     private static void OnColumnsChanged(DependencyObject pager, DependencyPropertyChangedEventArgs e) 
     { 
      (pager as Pager).Columns = (int)e.NewValue; 
     } 

    } 
} 

[Pager.xaml]

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:view="clr-namespace:Client.View" 
      xmlns:viewModel="clr-namespace:Client.ViewModel" 
      xmlns:resStr="clr-namespace:Client.CommonResources.String" 
      x:Class="Client.View.Pager"> 
    <Grid x:Name="gridPager"> 
     <Grid.DataContext> 
      <viewModel:PagerViewModel /> 
     </Grid.DataContext> 

     ... 

     <ListBox x:Name="listBoxEntries" 
       ItemsSource="{Binding Path=Collection}" 
       BorderThickness="0" 
       Margin="0" 
       Style="{StaticResource common}" 
       HorizontalContentAlignment="Stretch" 
       VerticalContentAlignment="Stretch" 
       ItemTemplate="{StaticResource templateTableCategory}" 
       SelectedItem="{Binding Path=SelectedEntry, Mode=TwoWay}" 
       SelectionChanged="ListBoxEntriesOnSelectionChanged"> 
      <ListBox.ItemsPanel> 
       <ItemsPanelTemplate> 
        <UniformGrid Rows="{Binding Path=Rows}" 
           Columns="{Binding Path=Columns}" 
           IsItemsHost="True"/> 
       </ItemsPanelTemplate> 
      </ListBox.ItemsPanel> 
     </ListBox> 

     ... 

    </Grid> 
</UserControl> 

[PagerViewModel.cs]

using System.Collections; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Linq; 
using System.Windows.Data; 
using System.Windows.Media; 
using Client.Model; 

namespace Client.ViewModel 
{ 
    public class PagerViewModel : ViewModelBase 
    { 

     ... 

     ListCollectionView _listCollectionView; 
     ObservableCollection<IPagableEntry> _collection; 
     int _rows; 
     int _columns; 

     public int Rows 
     { 
      get { return _rows; } 
      set 
      { 
       _rows = value; 
       OnPropertyChanged(); 
      } 
     } 

     public int Columns 
     { 
      get { return _columns; } 
      set 
      { 
       _columns = value; 
       OnPropertyChanged(); 
      } 
     } 

     public ListCollectionView ListCollectionView 
     { 
      get { return _listCollectionView; } 
      set 
      { 
       _listCollectionView = value; 
       OnPropertyChanged(); 
      } 
     } 

     public ObservableCollection<IPagableEntry> Collection 
     { 
      get 
      { 
       return _collection; 
      } 

      set 
      { 
       _collection = value; 
       OnPropertyChanged(); 
      } 
     } 

     ... 

    } 
} 
+0

基本上你想要的值从mainviewmodel传递给usercontrolviewmodel? – AnjumSKhan

+0

是的!这就是我最终想要的。 –

回答

0

有两个明显的问题用您发布的代码:

  1. OnXXXChanged()处理程序没有做任何事情。他们正在回应他们反过来试图设定的属性变化。即他们只是重申,他们被告知,而不是在一些不同的对象设置一个属性值的属性设置。
  2. 您正试图在不存在的属性设置绑定。即您的代码隐藏SetBinding()电话的目标是gridPager对象,这仅仅是一个Grid。它没有任何RowsColumns,或Source属性来设置。

承担,我们将使用绑定来实现这样的时刻,你有第三个问题:

  • 你会想结合两个不同源的属性的相同的目标属性。例如。 Pager.Rows已经是结合建立MainPage.xaml中,与PagerTableCategoriesRows作为源目标。它也不能成为来自任何其他对象的绑定目标(并且最重要的是,自身的循环绑定,如果使用代码试图执行的依赖项属性,这是唯一有意义的来源)。
  • 我不是的,即使这样的智慧完全清楚。这似乎是Pager元件可以只是直接绑定到Pager属性本身,由此从原始视图模型继承值,而不是保持一个第二,完全分开的,但预期将要完全相同的视图模型。

    但假设,有一些很好的理由,我简直不明白,你打算在这里使用两种不同的视图模型,并希望保持它们同步,在我看来,你应该能够得到它通过更改您的OnXXXChanged()处理程序来工作,以便他们直接设置视图模型值。例如: -

    public partial class Pager 
    { 
    
        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(ObservableCollection<IPagableEntry>), typeof(Pager), new PropertyMetadata(null, OnSourceChanged)); 
        public static readonly DependencyProperty RowsProperty = DependencyProperty.Register("Rows", typeof(int), typeof(Pager), new PropertyMetadata(1, OnRowsChanged)); 
        public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(int), typeof(Pager), new PropertyMetadata(1, OnColumnsChanged)); 
        public static readonly DependencyProperty SelectedEntryProperty = DependencyProperty.Register("SelectedEntry", typeof(object), typeof(Pager)); 
    
        public int Rows 
        { 
         get { return (int)GetValue(RowsProperty); } 
         set { SetValue(RowsProperty, value); } 
        } 
    
        public int Columns 
        { 
         get { return (int)GetValue(ColumnsProperty); } 
         set { SetValue(ColumnsProperty, value); } 
        } 
    
        public object SelectedEntry 
        { 
         get { return GetValue(SelectedEntryProperty); } 
         set { SetValue(SelectedEntryProperty, value); } 
    
        } 
    
        public ObservableCollection<IPagableEntry> Source 
        { 
         get { return (ObservableCollection<IPagableEntry>)GetValue(SourceProperty); } 
         set { SetValue(SourceProperty, value); } 
        } 
    
        public Pager() 
        { 
         InitializeComponent(); 
        } 
    
        private void ListBoxEntriesOnSelectionChanged(object sender, SelectionChangedEventArgs e) 
        { 
         SelectedEntry = (sender as ListBox).SelectedItem; 
        } 
    
        private static void OnSourceChanged(DependencyObject pager, DependencyPropertyChangedEventArgs e) 
        { 
         ((PagerViewModel)(pager as Pager).gridPager.DataContext).Collection = 
          (ObservableCollection<IPagableEntry>)e.NewValue; 
        } 
    
        private static void OnRowsChanged(DependencyObject pager, DependencyPropertyChangedEventArgs e) 
        { 
         ((PagerViewModel)(pager as Pager).gridPager.DataContext).Rows = 
          (int)e.NewValue; 
        } 
    
        private static void OnColumnsChanged(DependencyObject pager, DependencyPropertyChangedEventArgs e) 
        { 
         ((PagerViewModel)(pager as Pager).gridPager.DataContext).Columns = 
          (int)e.NewValue; 
        } 
    } 
    

    顺便说一句:我会阻止你在上面使用的as。主要原因是如果由于某种原因演员失败,一个不太有帮助的NullReferenceException将成为您的第一个可见症状,而不是InvalidCastException

    我推荐一个使用as只有当它是预计那一段时间,演员阵容会失败。当然,在这种情况下,您还需要总是检查null结果并正确处理。

    如果您打算,中投将始终成功,然后使用转换运算符。

    0

    该解决方案假定:

    1. 父窗口/页一无所知UC。但UC知道它的父母。
    2. UC依靠从它的父代传播到它的DataContext。

    这段代码的含义:

    1. UC创建一个2路与MainViewModel的MainWinProp1属性的绑定。
    2. 在UC中进行的任何更改都可以在MainViewModel中看到,反之亦然。

    是怎样做的?

    通过它自己的DataContext获取MainViewModel在UC。由于UC DataContext自动从parentwin dctx获取其值。但是可能会发生这样的情况,例如,您的UC存在于MainWin的某个Grid中,并且此Grid正在使用其他一些视图模型。在这种情况下,你必须使用一些VisualTree遍历帮助器方法来到达根窗口/页面以获取它的datacontext。

    https://www.dropbox.com/s/5ryc9ndxdu2m6a4/WpfApplication3.rar?dl=0

    如果你不想让你的UC,以取决于你的父母,而是父母使用UC,然后从父母,你总是可以轻松地访问UC和你想要做什么。

    +1

    请不要使用外部网站提供您的答案中的重要细节,不要介意实际的代码。堆栈溢出问题应该完全独立,不需要依赖外部网站,尤其是已知系统短暂的网站。如果代码对您的答案很重要,请提供[一个很好的_minimal_,_complete_代码示例](https://stackoverflow.com/help/mcve),它清楚地说明了您的答案,就像我们预期的问题一样。 –

    0

    您的主要困惑是因为Pager不是视图,而是UserControl

    他们都可以从UserControl类继承,但不同的是:在MVVM视图(或子视图又名DataTemplate)已绑定(通过DataContext)一个视图模型或它的母公司,但没有“功能”中“代码隐藏”。

    在另一侧的UserControl从来没有属于它一个视图模型(读:逻辑没有分裂成视图模型),因为UserControl意味着跨多个应用程序可以重复使用,视图是特定于ViewModel并且只能在您的应用程序中使用。

    UserControl中,隐藏代码是非常有效的(对于依赖属性,其他Application ViewModel可以绑定到或在代码背后登录)。 UserControl将公开DP以进行外部数据绑定。在View这是一个绝对不行,违反MVVM模式。

    这是一个非常常见的陷阱,开发人员MVVM遇到并尝试为控件创建ViewModel并发现自己停留在那里。

    话虽这么说,因为(恕我直言,从上面的例子我没有看到你的寻呼机任何应用程序特定的功能)您PagerUserControl它不需要PagerViewModel和它的代码应该在移动Pager的代码隐藏。

    在一个侧面说明 这应该是从你的寻呼机的ViewModel类明显,您尝试验证MVVM因为它保持强劲引用视图(不仅是视图类,但到任何 WPF相关班!)。

    using System.Collections; 
    using System.Collections.Generic; 
    using System.Collections.ObjectModel; 
    using System.Linq; 
    // MVVM Violation, it's part of WPF 
    using System.Windows.Data; 
    // MVVM Violation, it's part of WPF (usually PresentationFramework.dll) 
    using System.Windows.Media; 
    using Client.Model; 
    
    namespace Client.ViewModel 
    { 
        public class PagerViewModel : ViewModelBase 
        { 
    
         ... 
    
         // MVVM Violation, it's a type from Assembly: PresentationFramework (in PresentationFramework.dll) 
         ListCollectionView _listCollectionView; 
         ObservableCollection<IPagableEntry> _collection; 
         int _rows; 
         int _columns; 
    
         public int Rows 
         { 
          get { return _rows; } 
          set 
          { 
           _rows = value; 
           OnPropertyChanged(); 
          } 
         } 
    
         public int Columns 
         { 
          get { return _columns; } 
          set 
          { 
           _columns = value; 
           OnPropertyChanged(); 
          } 
         } 
    
         public ListCollectionView ListCollectionView 
         { 
          get { return _listCollectionView; } 
          set 
          { 
           _listCollectionView = value; 
           OnPropertyChanged(); 
          } 
         } 
    
         public ObservableCollection<IPagableEntry> Collection 
         { 
          get 
          { 
           return _collection; 
          } 
    
          set 
          { 
           _collection = value; 
           OnPropertyChanged(); 
          } 
         } 
    
         ... 
    
        } 
    } 
    

    如果您创建单独的程序集,则实施MVVM要容易得多。

    1. MyApp.Desktop/MyApp.UniversalApp/MyApp.Web:这允许有到PresentationFramework.dll和所有其他组件下面
    2. MyApp.ViewModels出处:该组件只允许包含ViewModels。仅引用模型,接口,域。参考Presentation.dll和MyApp.Data。* | MSSQL | Oracle | MySQL | SAP是严格禁止的。
    3. MyApp.Core/MyApp.Shared/MyApp.Domain:包含您的业务逻辑。参考文献模型和基础设施
    4. (可选)MyApp.Models:您的模型,没有别的。
    5. (可选)MyApp.Infrastructure/MyApp.Abstractions:包含您的服务接口(用于存储库或服务)。参考什么或只是模型
    6. (可选)MyApp.Data。* | MSSQL | Oracle | MySQL | SAP:你的数据库特定的实现和不属于你的域的一切。仅供参考,以基础设施和域

    ,如果你试图使用从Presentation.dll一个类型的模型或视图模型,它会失败,因为没有参照物,以组装和你即刻知道这是非常有帮助: “哇!停在这里。我做错了什么,它违反了MVVM!

    大胆强调组件必须能够运行和编译在其他平台(网络,桌面,WinPhone,Silverlight的) ,所以他们不允许有这个引用来查看特定的程序集。数据层可以不同于平台(即WinPhone应用程序可能希望使用SQLite而不是MSSQL,Linux上的ASP.NET网站可能更喜欢MySQL到MSSQL等)

    0

    正如我可以理解的,创建一个同步机制两个viewmodels。我完全赞同彼得的理论,但我建议你下一个同步解决方案。为了解决这个问题,我想建议你使用模型级别同步。只需将您需要的细节放入轻型模型类中,然后将此小模型插入所需的视图模型中即可。以下是方案: enter image description here

    问候,

    相关问题