2016-06-11 7 views
0

私は徐々にWPFのMVVMを学んでいます。 GBAのゲームエディタをコーディングします。エディタはメインウィンドウ(Editor.xaml)で構成され、どのエディタがメニューで選択されているかに応じて、対応する永続的なビュー(切り替え時には破壊者ではない)を表示したいと思います。永続的なナビゲーションビュー(MVVM、WPF)の管理

私はherehereのようなSOのいくつかの投稿にあるTabControlExクラスの作業をしようとしています。

しかし私は2つの問題に遭遇:最初、tabControlExのSelectedItemは(固定参照編集)を変更しないと第二、私が見るには(のTabItemの色Backgroundプロパティを持つことが白に切り替わります失うのTabItem ONMOUSEOVERに思えます色)。

私は、多かれ少なかれ明白なものを見逃していますが、MVVMを初めて知っていて、どこを見るべきか分かりません。ここでコードの内訳。

TabControlEx.cs

[TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))] 
public class TabControlEx : System.Windows.Controls.TabControl 
{ 
    // Holds all items, but only marks the current tab's item as visible 
    private Panel _itemsHolder = null; 

    // Temporaily holds deleted item in case this was a drag/drop operation 
    private object _deletedObject = null; 

    public TabControlEx() 
     : base() 
    { 
     // this is necessary so that we get the initial databound selected item 
     this.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; 
    } 

    /// <summary> 
    /// if containers are done, generate the selected item 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) 
    { 
     if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
     { 
      this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged; 
      UpdateSelectedItem(); 
     } 
    } 

    /// <summary> 
    /// get the ItemsHolder and generate any children 
    /// </summary> 
    public override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 
     _itemsHolder = GetTemplateChild("PART_ItemsHolder") as Panel; 
     UpdateSelectedItem(); 
    } 

    /// <summary> 
    /// when the items change we remove any generated panel children and add any new ones as necessary 
    /// </summary> 
    /// <param name="e"></param> 
    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) 
    { 
     base.OnItemsChanged(e); 

     if (_itemsHolder == null) 
     { 
      return; 
     } 

     switch (e.Action) 
     { 
      case NotifyCollectionChangedAction.Reset: 
       _itemsHolder.Children.Clear(); 

       if (base.Items.Count > 0) 
       { 
        base.SelectedItem = base.Items[0]; 
        UpdateSelectedItem(); 
       } 

       break; 

      case NotifyCollectionChangedAction.Add: 
      case NotifyCollectionChangedAction.Remove: 

       // Search for recently deleted items caused by a Drag/Drop operation 
       if (e.NewItems != null && _deletedObject != null) 
       { 
        foreach (var item in e.NewItems) 
        { 
         if (_deletedObject == item) 
         { 
          // If the new item is the same as the recently deleted one (i.e. a drag/drop event) 
          // then cancel the deletion and reuse the ContentPresenter so it doesn't have to be 
          // redrawn. We do need to link the presenter to the new item though (using the Tag) 
          ContentPresenter cp = FindChildContentPresenter(_deletedObject); 
          if (cp != null) 
          { 
           int index = _itemsHolder.Children.IndexOf(cp); 

           (_itemsHolder.Children[index] as ContentPresenter).Tag = 
            (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item)); 
          } 
          _deletedObject = null; 
         } 
        } 
       } 

       if (e.OldItems != null) 
       { 
        foreach (var item in e.OldItems) 
        { 

         _deletedObject = item; 

         // We want to run this at a slightly later priority in case this 
         // is a drag/drop operation so that we can reuse the template 
         this.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, 
          new Action(delegate() 
          { 
           if (_deletedObject != null) 
           { 
            ContentPresenter cp = FindChildContentPresenter(_deletedObject); 
            if (cp != null) 
            { 
             this._itemsHolder.Children.Remove(cp); 
            } 
           } 
          } 
         )); 
        } 
       } 

       UpdateSelectedItem(); 
       break; 

      case NotifyCollectionChangedAction.Replace: 
       throw new NotImplementedException("Replace not implemented yet"); 
     } 
    } 

    /// <summary> 
    /// update the visible child in the ItemsHolder 
    /// </summary> 
    /// <param name="e"></param> 
    protected override void OnSelectionChanged(SelectionChangedEventArgs e) 
    { 
     base.OnSelectionChanged(e); 
     UpdateSelectedItem(); 
    } 

    /// <summary> 
    /// generate a ContentPresenter for the selected item 
    /// </summary> 
    void UpdateSelectedItem() 
    { 
     if (_itemsHolder == null) 
     { 
      return; 
     } 

     // generate a ContentPresenter if necessary 
     TabItem item = GetSelectedTabItem(); 
     if (item != null) 
     { 
      CreateChildContentPresenter(item); 
     } 

     // show the right child 
     foreach (ContentPresenter child in _itemsHolder.Children) 
     { 
      child.Visibility = ((child.Tag as TabItem).IsSelected) ? Visibility.Visible : Visibility.Collapsed; 
     } 
    } 

    /// <summary> 
    /// create the child ContentPresenter for the given item (could be data or a TabItem) 
    /// </summary> 
    /// <param name="item"></param> 
    /// <returns></returns> 
    ContentPresenter CreateChildContentPresenter(object item) 
    { 
     if (item == null) 
     { 
      return null; 
     } 

     ContentPresenter cp = FindChildContentPresenter(item); 

     if (cp != null) 
     { 
      return cp; 
     } 

     // the actual child to be added. cp.Tag is a reference to the TabItem 
     cp = new ContentPresenter(); 
     cp.Content = (item is TabItem) ? (item as TabItem).Content : item; 
     cp.ContentTemplate = this.SelectedContentTemplate; 
     cp.ContentTemplateSelector = this.SelectedContentTemplateSelector; 
     cp.ContentStringFormat = this.SelectedContentStringFormat; 
     cp.Visibility = Visibility.Collapsed; 
     cp.Tag = (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item)); 
     _itemsHolder.Children.Add(cp); 
     return cp; 
    } 

    /// <summary> 
    /// Find the CP for the given object. data could be a TabItem or a piece of data 
    /// </summary> 
    /// <param name="data"></param> 
    /// <returns></returns> 
    ContentPresenter FindChildContentPresenter(object data) 
    { 
     if (data is TabItem) 
     { 
      data = (data as TabItem).Content; 
     } 

     if (data == null) 
     { 
      return null; 
     } 

     if (_itemsHolder == null) 
     { 
      return null; 
     } 

     foreach (ContentPresenter cp in _itemsHolder.Children) 
     { 
      if (cp.Content == data) 
      { 
       return cp; 
      } 
     } 

     return null; 
    } 

    /// <summary> 
    /// copied from TabControl; wish it were protected in that class instead of private 
    /// </summary> 
    /// <returns></returns> 
    protected TabItem GetSelectedTabItem() 
    { 
     object selectedItem = base.SelectedItem; 
     if (selectedItem == null) 
     { 
      return null; 
     } 

     if (_deletedObject == selectedItem) 
     { 

     } 

     TabItem item = selectedItem as TabItem; 
     if (item == null) 
     { 
      item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem; 
     } 
     return item; 
    } 
} 

私のメインビュー:

Editor.xaml

<Window.Resources> 
    <ResourceDictionary Source="Resources/GlobalDictionary.xaml" /> 
</Window.Resources> 
<DockPanel> 
    <DockPanel DockPanel.Dock="Top" KeyboardNavigation.TabNavigation="None"> 
     <Menu Height="20" KeyboardNavigation.TabNavigation="Cycle"> 
      <MenuItem Header="{StaticResource MenuFile}"> 
       <MenuItem Header="{StaticResource MenuOpen}" /> 
       <MenuItem Header="{StaticResource MenuSave}" /> 
       <MenuItem Header="{StaticResource MenuExit}" Command="{Binding Path=CloseCommand}" /> 
      </MenuItem> 
      <MenuItem Header="{StaticResource MenuEditors}"> 
       <MenuItem Header="{StaticResource MenuMonster}" Command="{Binding Path=OpenMonsterEditor}"/> 
      </MenuItem> 
      <MenuItem Header="{StaticResource MenuHelp}"> 
       <MenuItem Header="{StaticResource MenuAbout}" /> 
       <MenuItem Header="{StaticResource MenuContact}" /> 
      </MenuItem> 
     </Menu> 
    </DockPanel> 
    <controls:TabControlEx ItemsSource="{Binding AvailableEditors}" 
       SelectedItem="{Binding CurrentEditor}" 
       Style="{StaticResource BlankTabControlTemplate}"> 
    </controls:TabControlEx> 
</DockPanel> 

GlobalDictionary.xaml

<DataTemplate DataType="{x:Type vm:GBARomViewModel}"> 
    <vw:GBARomView /> 
</DataTemplate> 

<DataTemplate DataType="{x:Type vm:MonsterViewModel}"> 
    <vw:MonsterView /> 
</DataTemplate> 

<Style x:Key="BlankTabControlTemplate" TargetType="{x:Type control:TabControlEx}"> 
    <Setter Property="SnapsToDevicePixels" Value="true"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type control:TabControlEx}"> 
       <DockPanel> 
        <!-- This is needed to draw TabControls with Bound items --> 
        <StackPanel IsItemsHost="True" Height="0" Width="0" /> 
        <Grid x:Name="PART_ItemsHolder" /> 
       </DockPanel> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

以下が正しいかどうかはわかりませんが、各ビューのエクステントTabItemは次のとおりです。起動時の最初のビューが正しく表示されます。

GBARomView.xaml

<TabItem x:Class="FF6AE.View.GBARomView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:local="clr-namespace:FF6AE.View" 
     mc:Ignorable="d" 
     d:DesignHeight="380" d:DesignWidth="646" 
     Background="BlueViolet"> 

    <Grid > 

    </Grid> 
</TabItem> 

GBARomView.xaml.cs

public partial class GBARomView : TabItem 
{ 
    public GBARomView() 
    { 
     InitializeComponent(); 
    } 
} 

最後に私のメインのViewModel:

EDitorViewModel.cs

public class EditorViewModel: ViewModelBase 
{ 
    ViewModelBase _currentEditor; 
    ObservableCollection<ViewModelBase> _availableEditors; 
    RelayCommand _closeCommand; 
    RelayCommand _OpenMonsEditorCommand; 

    public EditorViewModel() 
    { 
     base.DisplayName = (string)AppInst.GetResource("EditorName"); 

     _availableEditors = new ObservableCollection<ViewModelBase>(); 
     _availableEditors.Add(new GBARomViewModel()); 
     _availableEditors.Add(new MonsterViewModel()); 
     _availableEditors.CollectionChanged += this.OnAvailableEditorsChanged; 

     _currentEditor = _availableEditors[0]; 
     _currentEditor.PropertyChanged += this.OnSubEditorChanged; 
    } 

    public ViewModelBase CurrentEditor 
    { 
     get 
     { 
      if (_currentEditor == null) 
      { 
       _currentEditor = new GBARomViewModel(); 
       _currentEditor.PropertyChanged += this.OnSubEditorChanged; 
      } 

      return _currentEditor; 
     } 
     set 
     { 
      _currentEditor = value; 
      // this is the thing I was missing 
      OnPropertyChanged("CurrentEditor"); 
     } 
    } 

    void OnSubEditorChanged(object sender, PropertyChangedEventArgs e) 
    { 
     // For future use 
    } 

    public ObservableCollection<ViewModelBase> AvailableEditors 
    { 
     get 
     { 
      return _availableEditors; 
     } 
    } 

    void OnAvailableEditorsChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     // For future use 
    } 

    public ICommand OpenMonsterEditor 
    { 
     get 
     { 
      if (_OpenMonsEditorCommand == null) 
       _OpenMonsEditorCommand = new RelayCommand(param => this.OpenRequestOpen()); 

      return _OpenMonsEditorCommand; 
     } 
    } 
    void OpenRequestOpen() 
    { 
     _currentEditor = _availableEditors[1]; 
    } 

    public ICommand CloseCommand 
    { 
     get 
     { 
      if (_closeCommand == null) 
       _closeCommand = new RelayCommand(param => this.OnRequestClose()); 

      return _closeCommand; 
     } 
    } 

    public event EventHandler RequestClose; 

    void OnRequestClose() 
    { 
     EventHandler handler = this.RequestClose; 
     if (handler != null) 
      handler(this, EventArgs.Empty); 
    } 
} 

そこで、基本的私はcurrentEditor値がを変更(固定)と理由のTabItem上でマウスオーバーで私はの背景テスト色落ちしているが、なぜメニューのモンスターエディタをクリックすると、ビューを切り替えるしない失っていますビュー(白に変わります)。 < - まだ修正されていません!

ご協力いただければ幸いです。前もって感謝します。

編集:私はOnPropertyChanged( "CurrentEditor");セッターのCurrentEditorです。

答えて

0

基本的に私の2つの質問は誤解でした。まず、CurrentEditor属性には、次のようなEditorViewModel.csで呼び出されたOnPropertyChangedが必要です。これはViewModelBaseクラスOnPropertyChangedをメソッドを呼び出します。

public ViewModelBase CurrentEditor 
{ 
    get 
    { 
     if (_currentEditor == null) 
     { 
      _currentEditor = new GBARomViewModel(); 
      _currentEditor.PropertyChanged += this.OnSubEditorChanged; 
     } 

     return _currentEditor; 
    } 
    set 
    { 
     _currentEditor = value; 
     OnPropertyChanged("CurrentEditor"); 
    } 
} 

私の第二の問題は、解決に時間がかかりました。私はビューの範囲をTabItemにする必要はなく、DockPanelのようなものでした。これにより、ウィンドウ全体が、色をデフォルトに戻すTabItem IsMouseOver動作を持たないようになります。フォロー作業などの背景イメージを設定すると、そのビューをナビゲートできます。私はTabItemのコントロールテンプレートでこれを解決することができませんでした。

GbaRomView.xaml.cs

public partial class GBARomView : DockPanel 
{ 
    public GBARomView() 
    { 
     InitializeComponent(); 
    } 
} 

GbaRomView.xaml

<DockPanel x:Class="FF6AE.View.GBARomView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     d:DesignHeight="380" d:DesignWidth="646"> 
    <DockPanel.Background> 
     <ImageBrush ImageSource="/Resources/Images/DefaultBackground.png"></ImageBrush> 
    </DockPanel.Background> 
</DockPanel>