2010-07-23 4 views
1

WPF의 바인딩 프로세스를 이해하려고 시도합니다.WPF 간단한 바인딩 문제

아래 코드를 참조하십시오.

내 "viewmodel"에는 아래쪽에있는 코드를 참조하십시오. 목록보기에 항목을 채우는 관찰 가능한 컬렉션이 있습니다. 콤보 박스에서 선택된 인덱스를 설정하기 위해 symbol이라는 경로를 포함하고있는 것입니다. 이제 내 문제는 내가 listview (일부 기본값)에 추가하기 전에 다른 목록에서 콤보 상자를 채울 필요가 있다는 것입니다. WPF로 시작한 이래로 같은 클래스에서 ObservableCollections를 두 가지 사용할 수 있다고 생각했지만 작동하지 않았습니다. 그래서 어떻게 그들이 목록보기에 바인딩/추가되기 전에 datatemplate을 기본값으로 채울 수 있습니까?

이것은 내가 listview를 채우기 위해 사용하는 것으로, 하단의 viewmodelcontacts를 참조하십시오.

또한 새로운 combobox에서 사용할 수있는 observablecollection과 함께 다른 클래스를 추가하려고했지만 그 중 하나를 작동시키지 못했습니다. 채워야하는 데이터는 내 앱의 리소스로 위치한 XML 파일에서 가져옵니다.

또 다른 질문은 이미지에 명령을 추가 할 수 있습니까? 또는 button_base 클래스에서 상속 한 컨트롤에서만 사용할 수있는 명령입니까? 나는 요소 옆에있는 이미지를 사용하고 싶었고 사용자가 그 이미지를 클릭했을 때 요소를 제거했다. 내가 버튼 느낌을하지 않기 때문에 (예를 들어 마우스를 올려 놓고 클릭 할 때) * 아래의 대답에서

Window.xaml 버튼을 추가하지 않고 그것을 할 수있다 :

<r:RibbonWindow x:Class="Onyxia_KD.Windows.ContactWorkspace" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"   
    Title="Contact card" ResizeMode="NoResize" Height="600" Width="600" 
    Background="White"> 
<r:RibbonWindow.Resources> 
    <DataTemplate x:Key="cardDetailFieldTemplate"> 
     <TextBox Text="{Binding Path=Field}" MinWidth="150"></TextBox> 
    </DataTemplate> 
    <DataTemplate x:Key="cardDetailValueTemplate"> 
     <TextBox Text="{Binding Path=Value}" MinWidth="150"></TextBox> 
    </DataTemplate> 
    <DataTemplate x:Key="cardDetailSymbolTemplate"> 
     <!-- Here is the problem. Populating this with some default values for each entry before the selectedIndex is bound from the datasource --> 
     <ComboBox SelectedIndex="{Binding Path=Symbol}" DataContext="_vmSettings" ItemsSource="{Binding Symbols}" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Center"> 
      <ListViewItem Padding="0,3,0,3" Content="{Binding Path=Value}" />          
     </ComboBox> 
    </DataTemplate> 
    <DataTemplate x:Key="cardDetailCategoryTemplate"> 
     <ComboBox SelectedIndex="{Binding Path=Category}" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Center"> 
      <!--same as the combobox above but categories instead of symbols-->     
     </ComboBox> 
    </DataTemplate> 
</r:RibbonWindow.Resources> 
... 
<ListView ItemsSource="{Binding ContactData}" Foreground="Black" SelectionMode="Single" x:Name="cardDetailList" KeyDown="cardDetailList_KeyDown"> 
        <ListView.View> 
         <GridView> 
          <GridViewColumn Header="Category" Width="auto" CellTemplate="{StaticResource cardDetailCategoryTemplate}"></GridViewColumn> 
          <GridViewColumn Header="Field" Width="auto" CellTemplate="{StaticResource cardDetailFieldTemplate}"></GridViewColumn> 
          <GridViewColumn Header="Symbol" Width="70" CellTemplate="{StaticResource cardDetailSymbolTemplate}"></GridViewColumn> 
          <GridViewColumn Header="Value" Width="auto" CellTemplate="{StaticResource cardDetailValueTemplate}"></GridViewColumn>         
         </GridView> 
        </ListView.View>       
       </ListView> 
뒤에

코드 :

private ViewModelContacts _vm; 

    public ContactWorkspace() 
    { 
     InitializeComponent(); 

     _vm = new ViewModelContacts();    
     this.DataContext = _vm; 

    } 

    private void Run_MouseUp(object sender, MouseButtonEventArgs e) 
    { 
     _vm.AddNewDetail(); 
    } 

    private void Image_MouseUp(object sender, MouseButtonEventArgs e) 
    { 
     _vm.AddNewDetail(); 
    } 

    private void cardDetailList_KeyDown(object sender, KeyEventArgs e) 
    { 
     if (e.Key == Key.Delete) 
     { 
      if (MessageBox.Show("Are you sure that you want to delete this?", "Warning!", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes) 
      { 
       _vm.RemoveDetail(cardDetailList.SelectedIndex); 
      } 
     } 
    } 

ViewModelContacts :

public ObservableCollection<ContactCardData> ContactData { get; set; }    

    public ViewModelContacts() 
    { 

     ContactData = new ObservableCollection<ContactCardData>();    
     Populate(); 
    } 

    private void Populate() 
    { 
     ContactData.Add(new ContactCardData("Test", 0, 0, "Value123")); 
     ContactData.Add(new ContactCardData("Test2", 1, 1, "Value1234")); 
     ContactData.Add(new ContactCardData("Test3", 2, 2, "Value1235")); 
     ContactData.Add(new ContactCardData("Test4", 3, 3, "Value12356"));    
    } 

    public void UpdateNode() 
    { 
     ContactData.ElementAt(0).Value = "Giraff"; 
    } 

    public void AddNewDetail() 
    { 
     ContactData.Add(new ContactCardData()); 
    } 

    public void RemoveDetail(int position) 
    { 
     ContactData.RemoveAt(position); 
    } 

ViewModelContactData : 마지막 질문에 대한

public class ContactCardData : DependencyObject 
{ 
    public int Category 
    { 
     get { return (int)GetValue(CategoryProperty); } 
     set { SetValue(CategoryProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Category. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty CategoryProperty = 
     DependencyProperty.Register("Category", typeof(int), typeof(ContactCardData), new UIPropertyMetadata(0)); 

    public string Field 
    { 
     get { return (string)GetValue(FieldProperty); } 
     set { SetValue(FieldProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Field. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty FieldProperty = 
     DependencyProperty.Register("Field", typeof(string), typeof(ContactCardData), new UIPropertyMetadata("")); 

    public string Value 
    { 
     get { return (string)GetValue(ValueProperty); } 
     set { SetValue(ValueProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ValueProperty = 
     DependencyProperty.Register("Value", typeof(string), typeof(ContactCardData), new UIPropertyMetadata("")); 

    public int Symbol 
    { 
     get { return (int)GetValue(SymbolProperty); } 
     set { SetValue(SymbolProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Symbol. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty SymbolProperty = 
     DependencyProperty.Register("Symbol", typeof(int), typeof(ContactCardData), new UIPropertyMetadata(0)); 

    public ContactCardData() 
    { 
    } 

    public ContactCardData(string field, int category, int symbol, string value) 
    { 
     this.Symbol = symbol; 
     this.Category = category; 
     this.Field = field; 
     this.Value = value; 
    } 
} 

답변

4

이 당신의 창 클래스를 정의 (설명은 나중에 할 것이다) :

<Window x:Class="TestCustomTab.Window1" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestCustomTab="clr-namespace:TestCustomTab" Title="Window1" Height="300" Width="300"> 
     <Window.Resources> 
      <DataTemplate x:Key="workingTemplate"> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="Auto"/> 
         <ColumnDefinition Width="*"/> 
        </Grid.ColumnDefinitions> 
        <TextBlock Grid.Column="0" Text="{Binding Name}"/> 
        <ComboBox Grid.Column="1" SelectedIndex="0" ItemsSource="{Binding Symbols}"> 
         <ComboBox.ItemTemplate> 
          <DataTemplate> 
           <Grid> 
            <Grid.ColumnDefinitions> 
             <ColumnDefinition Width="Auto"/> 
             <ColumnDefinition Width="*"/> 
            </Grid.ColumnDefinitions> 
            <Ellipse Grid.Column="0" Fill="Red" Height="5" Width="5"/> 
            <TextBlock Grid.Column="1" Text="{Binding}" /> 
           </Grid> 
          </DataTemplate> 
         </ComboBox.ItemTemplate> 
        </ComboBox> 
       </Grid> 
      </DataTemplate> 

      <DataTemplate x:Key="personalTemplate"> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="Auto"/> 
         <ColumnDefinition Width="*"/> 
        </Grid.ColumnDefinitions> 
        <TextBlock Grid.Column="0" Text="{Binding Name}"/> 
        <ComboBox Grid.Column="1" SelectedIndex="0" ItemsSource="{Binding Symbols}"> 
         <ComboBox.ItemTemplate> 
          <DataTemplate> 
           <Grid> 
            <Grid.ColumnDefinitions> 
             <ColumnDefinition Width="Auto"/> 
             <ColumnDefinition Width="*"/> 
            </Grid.ColumnDefinitions> 
            <Ellipse Grid.Column="0" Fill="Green" Height="5" Width="5"/> 
            <TextBlock Grid.Column="1" Text="{Binding}" /> 
           </Grid> 
          </DataTemplate> 
         </ComboBox.ItemTemplate> 
        </ComboBox> 
       </Grid> 
      </DataTemplate> 

      <TestCustomTab:ContactDataByTypeTemplateSelector x:Key="contactDataByTypeTemplateSelector"/> 
     </Window.Resources> 
     <Grid x:Name="grid">   
      <ListView ItemsSource="{Binding ContactDataCollection, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TestCustomTab:Window1}}}"> 
       <ListView.View> 
        <GridView> 
         <GridViewColumn Header="Column" Width="200" CellTemplateSelector="{StaticResource contactDataByTypeTemplateSelector}"> 

         </GridViewColumn> 
        </GridView> 
       </ListView.View> 
      </ListView> 
     </Grid> 
    </Window> 

는 다음과 같이 연락처 데이터 보인다 있다고 가정하자 :

public class ContactData : INotifyPropertyChanged 
    { 
     private string _name; 
     private int _homePhone; 
     private int _mobilePhone; 
     private string _value; 
     private ContactDataType _dataType; 

     public ContactData() 
     { 
     } 

     public ContactData(string name, int homePhone, int mobilePhone, string value, ContactDataType dataType) 
     { 
      _name = name; 
      _dataType = dataType; 
      _value = value; 
      _mobilePhone = mobilePhone; 
      _homePhone = homePhone; 
     } 

     #region Implementation of INotifyPropertyChanged 

     public ContactDataType DataType 
     { 
      get { return _dataType; } 
      set 
      { 
       if (_dataType == value) return; 
       _dataType = value; 
       raiseOnPropertyChanged("DataType"); 
      } 
     } 

     public string Name 
     { 
      get { return _name; } 
      set 
      { 
       if (_name == value) return; 
       _name = value; 
       raiseOnPropertyChanged("Name"); 
      } 
     } 

     public int HomePhone 
     { 
      get { return _homePhone; } 
      set 
      { 
       if (_homePhone == value) return; 
       _homePhone = value; 
       raiseOnPropertyChanged("HomePhone"); 
      } 
     } 

     public int MobilePhone 
     { 
      get { return _mobilePhone; } 
      set 
      { 
       if (_mobilePhone == value) return; 
       _mobilePhone = value; 
       raiseOnPropertyChanged("MobilePhone"); 
      } 
     } 

     public string Value 
     { 
      get { return _value; } 
      set 
      { 
       if (_value == value) return; 
       _value = value; 
       raiseOnPropertyChanged("Value"); 
       raiseOnPropertyChanged("Symbols"); 
      } 
     } 

     public ReadOnlyCollection<char> Symbols 
     { 
      get 
      { 
       return !string.IsNullOrEmpty(_value) ? new ReadOnlyCollection<char>(_value.ToCharArray()) : new ReadOnlyCollection<char>(new List<char>()); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     private void raiseOnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 

     #endregion 
    } 

이 형 ContactDataType의 데이터 형식 속성을 열거입니다있다 :

public enum ContactDataType 
    { 
     Working, 
     Personal 
    } 

다른 DataTemplates DataTemplateSelector를 사용해야하는 일부 기능으로 구분 된 동일한 엔티티의 경우 이 기술은 DataTemplateSelector에서 상속 받고 SelectTemplate 메서드를 재정의하는 것입니다.우리의 경우 : 여기

public class ContactDataByTypeTemplateSelector : DataTemplateSelector 
    { 
     public override DataTemplate SelectTemplate(object item, DependencyObject container) 
     { 
      var contactData = item as ContactData; 
      var control = container as FrameworkElement; 
      if (contactData != null & control != null) 
       switch (contactData.DataType) 
       { 
        case ContactDataType.Working: 
         return control.TryFindResource("workingTemplate") as DataTemplate; 
        case ContactDataType.Personal: 
         return control.TryFindResource("personalTemplate") as DataTemplate; 
        default: 
         return base.SelectTemplate(item, container); 
       } 

      return base.SelectTemplate(item, container); 
     } 
    } 

는 번째 윈도 클래스는 코드 숨김에 있습니다

public partial class Window1 : Window 
    { 
     private ObservableCollection<ContactData> _contactDataCollection = new ObservableCollection<ContactData>(); 

     public Window1() 
     { 
      ContactDataCollection.Add(new ContactData("test1", 0, 1, "value1", ContactDataType.Working)); 
      ContactDataCollection.Add(new ContactData("test2", 0, 1, "value2", ContactDataType.Working)); 
      ContactDataCollection.Add(new ContactData("test3", 0, 1, "value3", ContactDataType.Working)); 
      ContactDataCollection.Add(new ContactData("test4", 0, 1, "value4", ContactDataType.Personal)); 
      ContactDataCollection.Add(new ContactData("test5", 0, 1, "value5", ContactDataType.Personal)); 

      InitializeComponent(); 

     } 


     public ObservableCollection<ContactData> ContactDataCollection 
     { 
      get { return _contactDataCollection; } 
     } 
    } 

지금 설명 : 우리는 우리가 사용자에게 표현하기 위해 필요한 몇 가지 클래스를 만들었

  1. (ContactData) 그에게 기능 - ContactDataType을 갖도록하십시오.
  2. 우리는 ContactDataType.Working을위한 자원이 DataTemplates을 (x:Key이 중요) 작성

  3. ContactDataType.Personal 우리는 기능으로 기능 스위치 템플릿을 가지고 DataTemplateSelector를 만들었습니다.

  4. GridViewColumn 우리는 CellTemplateSelector을 정의하고 우리의 ContactDataByTypeTemplateSelector에 바인딩합니다.

  5. 컬렉션이 변경 될 때마다 런타임에 ContactDataByTypeTemplateSelector 항목 기능을 기반으로하는 템플릿을 선택하면 모든 정의 된 기능에 대해 템플릿 수가 제한 될 수 있습니다.

알림 : 네임 스페이스에 대해 TestCustomTab을 변경하십시오.

+0

안녕하세요, 응답 해 주셔서 감사합니다. 나는 나 자신을 100 % 설명하지 않았다. listview 및 내 코드에 대한 XAML을 추가했는데, 그 중 일부는 작동하지 않는 심볼이었습니다. 나는 무엇을 묶을 지 전혀 모른다. 모든 미리 정의 된 XML 콘텐츠를 외부 클래스에 추가하고 모든 미리 정의 된 데이터 바인딩에 사용하고 싶습니다. 하지만 어떻게 동시에 두 datacontexts에 콤보 상자를 바인딩 할 수 있습니까? 하나는 현재 선택된 인덱스를 적용하고 다른 하나는 디폴트 값으로 표시하기 전에 콤보 박스를 채우기위한 것입니다. – Patrick

1

, 당신은 버튼을 사용하여 이미지를 포함하도록 템플릿 수 있습니다. 다음과 같이