2015-10-18 327 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

    問候,

    相關問題