2015-04-23 78 views
3

我期待有一個MenuItem,其內部CheckBox的屬性「IsThreeState」設置爲true。我想將它綁定到布爾?在我的ViewModel中。如何在WPF MenuItem複選框中設置IsThreeState = True?

看了一下後,我找到了found這個IsChecked是一個普通的老布爾。

我的第一本能是添加一個附加的布爾? 「IsCheckedThreeState」屬性到MenuItem,但我仍然無法弄清楚如何解決內部CheckBox綁定到不可爲空的IsChecked這一事實。

如果沒有更簡單的方法,我可以創建一個新的控件模板並直接修改CheckBox,但我認爲這也需要子類MenuItem來調整IsChecked爲空。

所以,我將不得不繼承/定製MenuItem模板以獲得我想要的功能,還是有一種我沒有想到的更簡單的方法。感謝您可能提供的任何幫助。

+0

嗯。一個多小時,沒有迴應。我發現最好的是「[*如何設置/重置WPF *中的三狀態複選框值](http://stackoverflow.com/q/4778230/1364007)」答案顯示如何*編程*設置值爲'null',但不啓用用戶的三態行爲。據推測,你已經看到了這個問題 - 它不直接幫助你,但認爲你可能會發現它的一些有用的。 –

回答

1

好的,所以我做了一些更多的挖掘。

MenuItem的控件模板甚至沒有內部的CheckBox控件。 MenuItem使用它自己的IsChecked屬性,並顯示或隱藏內部Path控件以指示其狀態。

所以,我改變了默認的控制模板。我用CheckBox替換Path,然後我重新連線了觸發器:

<ControlTemplate x:Key="CustomMenuItemControlTemplate" TargetType="{x:Type MenuItem}"> 
    <Grid SnapsToDevicePixels="True" MinWidth="225" MinHeight="26"> 
     <Rectangle x:Name="OuterBorder" RadiusY="2" RadiusX="2"/> 
     <Rectangle x:Name="Bg" Fill="{TemplateBinding Background}" Margin="1" RadiusY="1" RadiusX="1" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="1"/> 
     <Rectangle x:Name="InnerBorder" Margin="2"/> 
     <DockPanel> 
      <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" Margin="4,0,6,0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/> 
      <CheckBox x:Name="MenuCheckBox" Margin="7,4" Visibility="Hidden" VerticalAlignment="Center" IsThreeState="True" /> 
      <ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" /> 
     </DockPanel> 
     <Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" HorizontalOffset="1" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom" VerticalOffset="-1"> 
      <Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent"> 
       <Border x:Name="SubMenuBorder" BorderBrush="#FF959595" BorderThickness="1" Background="WhiteSmoke"> 
        <ScrollViewer x:Name="SubMenuScrollViewer" Margin="1,0" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}"> 
         <Grid RenderOptions.ClearTypeHint="Enabled"> 
          <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0"> 
           <Rectangle x:Name="OpaqueRect" Fill="WhiteSmoke" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/> 
          </Canvas> 
          <Rectangle Fill="#FFF1F1F1" HorizontalAlignment="Left" Margin="1,2" RadiusY="2" RadiusX="2" Width="28"/> 
          <Rectangle Fill="#FFE2E3E3" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/> 
          <Rectangle Fill="White" HorizontalAlignment="Left" Margin="30,2,0,2" Width="1"/> 
          <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" Margin="2" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/> 
         </Grid> 
        </ScrollViewer> 
       </Border> 
      </Themes:SystemDropShadowChrome> 
     </Popup> 
    </Grid> 
    <ControlTemplate.Triggers> 
     <Trigger Property="IsSuspendingPopupAnimation" Value="True"> 
      <Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/> 
     </Trigger> 
     <Trigger Property="Icon" Value="{x:Null}"> 
      <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/> 
     </Trigger> 
     <Trigger Property="IsCheckable" Value="True"> 
      <Setter Property="Visibility" TargetName="MenuCheckBox" Value="Visible"/> 
      <Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/> 
     </Trigger> 
     <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="True"> 
      <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/> 
      <Setter Property="Color" TargetName="Shdw" Value="#71000000"/> 
     </Trigger> 
     <Trigger Property="IsKeyboardFocused" Value="True"> 

      <Setter Property="Fill" TargetName="Bg"> 
       <Setter.Value> 
        <LinearGradientBrush EndPoint="0,1" StartPoint="0,0"> 
         <GradientStop Color="#0462c9f5" Offset="0"/> 
         <GradientStop Color="#1C62c9f5" Offset="0.75"/> 
         <GradientStop Color="#3062c9f5" Offset="1"/> 
        </LinearGradientBrush> 
       </Setter.Value> 
      </Setter> 
      <Setter Property="Stroke" TargetName="OuterBorder" Value="#C062c9f5"/> 
     </Trigger> 
     <Trigger Property="IsEnabled" Value="False"> 
      <Setter Property="Foreground" Value="#FF9A9A9A"/> 
     </Trigger> 
     <Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False"> 
      <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/> 
      <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/> 
     </Trigger> 
    </ControlTemplate.Triggers> 
</ControlTemplate> 

我在它自己的資源字典中有這個。你必須添加在您的項目「PresentationFramework.Aero」下面的xmlns參考和:

xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" 

然後,我電匯了這一切的風格:

<Style TargetType="MenuItem"> 
    <Style.Resources> 
     <Style TargetType="{x:Type CheckBox}"> 
      <Setter Property="IsChecked" Value="{Binding IsSelected}" /> 
     </Style> 
    </Style.Resources> 
    <Setter Property="Template" Value="{StaticResource CustomMenuItemControlTemplate}" /> 
    <Setter Property="IsCheckable" Value="True" /> 
    <Setter Property="IsChecked" Value="{Binding ToggleIsSelected}" /> 
    <Setter Property="StaysOpenOnClick" Value="True" /> 
</Style> 

最後一塊難題是綁定。我現在有兩個屬性:

1)IsSelected - a bool?我正在結合內部CheckBox的器isChecked財產

2)ToggleIsSelected - 一個布爾這我綁定到菜單項的器isChecked財產

有了這兩個綁定,用戶可以在任何地方點擊MenuItem切換CheckBox,如果IsSelected爲null,則不會有任何綁定錯誤。屬性定義爲:

public bool? IsSelected 
{ 
    get 
    { 
     return _isSelected; 
    } 
    set 
    { 
     if (value == _isSelected) 
      return; 
     _isSelected = value ?? false; 
     OnPropertyChanged(); 
     OnPropertyChanged("ToggleIsSelected"); 
    } 
} 

public bool ToggleIsSelected 
{ 
    get 
    { 
     return IsSelected ?? false; 
    } 
    set 
    { 
     if (value == IsSelected) 
      return; 
     IsSelected = value; 
    } 
} 

一個潛在的缺點做這種方式是,用戶無法切換複選框,以它爲空狀態。在我的情況下,我只想編程將其設置爲空,所以它適用於我。如果您需要用戶切換爲null,請從您的ViewModel和相關設置器中刪除ToggleIsSelected屬性:

<Setter Property="IsChecked" Value="{Binding ToggleIsSelected}" /> 

...和IsSelected屬性更改爲:

public bool? IsSelected 
{ 
    get 
    { 
     return _isSelected; 
    } 
    set 
    { 
     if (value == _isSelected) 
      return; 
     _isSelected = value; 
    OnPropertyChanged(); 
    } 
} 

這也將刪除到任意位置單擊切換的能力,讓您的用戶將不得不直接選中該複選框...除非你拿出來實施它的一些其他的方式。

我知道這是一個很長的答案......但我想其他人可能需要這個。如果可以的話,不妨節省時間。如果您能看到有待改進的空間,請告訴我。

1

最簡單的選擇可能是通過Icon屬性插入複選框。

<MenuItem Header="..." StaysOpenOnClick="True" Click="MenuItem_ToggleCheckBox"> 
    <MenuItem.Icon> 
     <CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsThreeState="True" /> 
    </MenuItem.Icon> 
</MenuItem> 

Click事件處理程序,你需要手動更新的複選框(或某些屬性,該屬性複選框綁定)。

+0

我可以看到這將如何工作,並且不需要定製的控制模板。不知道該圖標可以是任何控件。人們應該意識到的一個可能的問題是,MenuItem的IsCheckable和IsChecked不能爲真。當任何一個爲真時,控制模板將隱藏圖標。感謝您分享這個巧妙的小技巧。 – Chronicide

相關問題