2017-05-17 92 views
0

我有一个WPF自定义控件,其中我有一个ListView。该控件具有ListView的ItemSource和ItemTemplate的依赖属性。这一切工作正常。我想要做的是能够设置一个默认的ItemTemplate,这样我就不会在Listview中为Items添加object.ToString()。指定默认ItemTemplate

下面是我的控件的Xaml样式。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
       xmlns:btl="clr-namespace:Btl.Controls" 
       mc:Ignorable="d"> 

<DataTemplate x:Key="DefaultListViewItem" DataType="btl:SelectableItem"> 
    <StackPanel Orientation="Horizontal"> 
     <CheckBox Margin="2" IsChecked="{Binding Selected}" /> 
     <TextBlock Margin="5,2" Text="{Binding Description}" VerticalAlignment="Center"/> 
    </StackPanel> 
</DataTemplate> 
<Style TargetType="{x:Type btl:SelectItemsControl}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type btl:SelectItemsControl}"> 
       <Grid> 
        <Grid.RowDefinitions> 
         <RowDefinition Height="Auto"/> 
         <RowDefinition Height="Auto"/> 
         <RowDefinition Height="*"/> 
         <RowDefinition Height="Auto"/> 
        </Grid.RowDefinitions> 
        <TextBlock x:Name="PART_Title" Margin="10" 
           Text="{Binding Path=Title, 
             RelativeSource={RelativeSource TemplatedParent}}" 
           TextWrapping="Wrap" 
           Visibility="Collapsed"/> 
        <GroupBox Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
         <GroupBox.Header> 
          <StackPanel Orientation="Horizontal"> 
           <CheckBox x:Name="PART_EnabledCheck" Margin="0,5" 
              Content="" 
              IsChecked="{Binding Path=EnabledCheck, Mode=TwoWay, 
              RelativeSource={RelativeSource TemplatedParent}}"/> 
           <TextBlock x:Name="PART_GroupTitle" VerticalAlignment="Center" 
              Text="{Binding Path=GroupTitle, 
             RelativeSource={RelativeSource TemplatedParent}}"/> 
          </StackPanel> 
         </GroupBox.Header> 
         <Grid> 
          <Grid.RowDefinitions> 
           <RowDefinition Height="*"/> 
           <RowDefinition Height="Auto"/> 
          </Grid.RowDefinitions> 
          <DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > 
           <ListView x:Name="PART_Items" 
              ItemsSource="{Binding ItemSourceList, 
               RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type btl:SelectItemsControl}}}" 
              ItemTemplate="{Binding ItemTemplate, 
               RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type btl:SelectItemsControl}}}" 
              HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2" 
              > 
           </ListView> 
          </DockPanel> 
          <CheckBox x:Name="PART_SelectAllCheck" Grid.Row="1" Margin="11,5" Content="Select All" 
             IsChecked="{Binding Selected}"/> 
         </Grid> 
        </GroupBox> 

       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

这里是我的控制

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Media; 

namespace Btl.Controls 
{ 
/// <summary> 
/// 
/// </summary> 
/// 
[TemplatePart(Name = "PART_Title", Type = typeof(TextBlock))] 
[TemplatePart(Name = "PART_EnabledCheck", Type = typeof(CheckBox))] 
[TemplatePart(Name = "PART_GroupTitle", Type = typeof(TextBlock))] 
[TemplatePart(Name = "PART_Items", Type = typeof(ListView))] 
[TemplatePart(Name = "PART_SelectAllCheck", Type = typeof(CheckBox))] 
public class SelectItemsControl : UserControl 
{ 

    #region DependencyProperties 

    #region Title 
    public string Title 
    { 
     get { return (string)GetValue(TitleProperty); } 
     set { SetValue(TitleProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty TitleProperty = 
     DependencyProperty.Register("Title", typeof(string), typeof(SelectItemsControl), new PropertyMetadata(string.Empty, 
      OnTitleChanged 
      )); 

    private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 

     var control = d as TextBlock; 
     if (control != null) 
      if (string.IsNullOrEmpty(e.NewValue.ToString())) 
     { 
      control.Visibility = string.IsNullOrEmpty(e.NewValue.ToString()) ? Visibility.Collapsed : Visibility.Visible; 
     } 

    } 


    #endregion 

    #region HasEnabledCheck 
    public bool HasEnabledCheck 
    { 
     get { return (bool)GetValue(HasEnabledCheckProperty); } 
     set { SetValue(HasEnabledCheckProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for EnabledCheck. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty HasEnabledCheckProperty = 
     DependencyProperty.Register("HasEnabledCheck", typeof(bool), typeof(SelectItemsControl), new UIPropertyMetadata(false)); 
    #endregion 

    #region EnabledCheck 
    public bool EnabledCheck 
    { 
     get { return (bool)GetValue(EnabledCheckProperty); } 
     set { SetValue(EnabledCheckProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for EnabledCheck. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty EnabledCheckProperty = 
     DependencyProperty.Register("EnabledCheck", typeof(bool), typeof(SelectItemsControl), new UIPropertyMetadata(true)); 
    #endregion 

    #region GroupTitle 
    public string GroupTitle 
    { 
     get { return (string)GetValue(GroupTitleProperty); } 
     set { SetValue(GroupTitleProperty, value); } 
    } 

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

    #endregion 

    #region ItemSourceList 
    public IEnumerable<ISelectable> ItemSourceList 
    { 
     get { return (IEnumerable<ISelectable>)GetValue(ItemSourceListProperty); } 
     set { SetValue(ItemSourceListProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Items. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ItemSourceListProperty = 
     DependencyProperty.Register("ItemSourceList", typeof(IEnumerable), typeof(SelectItemsControl)); 


    #endregion 

    #region ItemTemplate 

    public DataTemplate ItemTemplate 
    { 
     get { return (DataTemplate)GetValue(ItemTemplateProperty); } 
     set { SetValue(ItemTemplateProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ItemTemplateProperty = 
     DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(SelectItemsControl), 
     new UIPropertyMetadata(default(DataTemplate))); 

    #endregion 


    #region DescriptionTemplate 

    public DataTemplate DescriptionTemplate 
    { 
     get { return (DataTemplate)GetValue(DescriptionTemplateProperty); } 
     set { SetValue(DescriptionTemplateProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty DescriptionTemplateProperty = 
     DependencyProperty.Register("DescriptionTemplate", typeof(DataTemplate), typeof(SelectItemsControl), 
     new UIPropertyMetadata(default(DataTemplate), OnDescriptionTemplateChanged)); 

    private static void OnDescriptionTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
    } 

    #endregion 

    #region ItemSelected 

    public bool ItemSelected 
    { 
     get { return (bool)GetValue(ItemSelectedProperty); } 
     set { SetValue(ItemSelectedProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for EnabledCheck. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ItemSelectedProperty = 
     DependencyProperty.Register("ItemSelectedCheck", typeof(bool), typeof(SelectItemsControl), new UIPropertyMetadata(false)); 

    #endregion 

    #region ItemDescription 



    public string ItemDescription 
    { 
     get { return (string)GetValue(ItemDescriptionProperty); } 
     set { SetValue(ItemDescriptionProperty, value); } 
    } 

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


    #endregion 

    #region SelectAllCheck 
    public bool SelectAll 
    { 
     get { return (bool)GetValue(SelectAllProperty); } 
     set { SetValue(SelectAllProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for SelectAll. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty SelectAllProperty = 
     DependencyProperty.Register("SelectAll", typeof(bool), typeof(SelectItemsControl), new UIPropertyMetadata(false)); 


    #endregion 

    #endregion 

    #region Private Members 

    private TextBlock _partTitle; 
    private CheckBox _partEnabledCheck; 
    private TextBlock _partGroupTitle; 
    private ListView _partItemsListView; 
    private CheckBox _partSelectAllCheck; 

    #endregion 

    /// <summary> 
    /// 
    /// </summary> 
    static SelectItemsControl() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata(typeof(SelectItemsControl), 
      new FrameworkPropertyMetadata(typeof(SelectItemsControl))); 
    } 

    public SelectItemsControl() 
    { 
     Loaded += OnLoaded; 
     Unloaded += OnUnloaded; 
    } 

    private void OnLoaded(object sender, RoutedEventArgs routedEventArgs) 
    { 
     if (ItemTemplate == null) 
     { 
      CreateDefaultItemTemplate(); 
     } 
     PresentationSource presentationSource = PresentationSource.FromVisual((Visual)sender); 

     // Subscribe to PresentationSource's ContentRendered event 
     // ReSharper disable once PossibleNullReferenceException 
     presentationSource.ContentRendered += SelectItemsControl_ContentRendered; 
    } 

    private void SelectItemsControl_ContentRendered(object sender, EventArgs e) 
    { 
     // Don't forget to unsubscribe from the event 
     ((PresentationSource)sender).ContentRendered -= SelectItemsControl_ContentRendered; 
     ListenToSelectedCheckBoxClickEvent(_partItemsListView, true); 
    } 

    private void OnUnloaded(object sender, RoutedEventArgs e) 
    { 
     ListenToSelectedCheckBoxClickEvent(_partItemsListView, false); 
    } 

    private void CreateDefaultItemTemplate() 
    { 
     DataTemplate template = new DataTemplate { DataType = typeof(ListViewItem) }; 
     FrameworkElementFactory stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel)); 
     stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal); 

     FrameworkElementFactory selected = new FrameworkElementFactory(typeof(CheckBox)); 
     selected.SetBinding(TextBlock.TextProperty, new Binding("Selected")); 
     stackPanelFactory.AppendChild(selected); 

     FrameworkElementFactory title = new FrameworkElementFactory(typeof(TextBlock)); 
     title.SetBinding(TextBlock.TextProperty, new Binding("Description")); 
     stackPanelFactory.AppendChild(title); 
    } 

    public override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 

     // Code to get the Template parts as instance member 
     _partTitle = GetTemplateChild("PART_Title") as TextBlock; 
     _partEnabledCheck = GetTemplateChild("PART_EnabledCheck") as CheckBox; 
     _partGroupTitle = GetTemplateChild("PART_GroupTitle") as TextBlock; 
     _partItemsListView = GetTemplateChild("PART_Items") as ListView; 
     //_partItemSelectedCheck = GetTemplateChild("PART_ItemSelectedCheck") as CheckBox; 
     _partSelectAllCheck = GetTemplateChild("PART_SelectAllCheck") as CheckBox; 

     if (_partTitle == null || _partEnabledCheck == null || _partGroupTitle == null || _partItemsListView == null || 
      _partSelectAllCheck == null) 
     { 
      throw new NullReferenceException("Template parts not available"); 
     } 

     // set visibility 
     _partEnabledCheck.Visibility = HasEnabledCheck ? Visibility.Visible : Visibility.Collapsed; 
     _partEnabledCheck.Click += PartEnabledCheckOnClick; 
     _partTitle.Visibility = string.IsNullOrEmpty(_partTitle.Text) ? Visibility.Collapsed : Visibility.Visible; 
     _partGroupTitle.Visibility = string.IsNullOrEmpty(_partGroupTitle.Text) ? Visibility.Collapsed : Visibility.Visible; 
     _partSelectAllCheck.Click += PartSelectAllCheckOnClick; 

    } 

    private void PartEnabledCheckOnClick(object sender, RoutedEventArgs routedEventArgs) 
    { 
     _partItemsListView.IsEnabled = EnabledCheck; 
     _partSelectAllCheck.IsEnabled = EnabledCheck; 
    } 

    private void ListenToSelectedCheckBoxClickEvent(DependencyObject parent, bool set) 
    { 
     foreach (CheckBox cb in VisualTreeHelpers.FindVisualChildren<CheckBox>(parent)) 
     { 
      BindingExpression binding = cb.GetBindingExpression(CheckBox.IsCheckedProperty); 
      // ReSharper disable once PossibleNullReferenceException 
      if (binding.ParentBinding.Path.Path == "Selected") 
      { 
       if (set) 
        cb.Click += SelectedCheckBox_Click; 
       else 
        cb.Click -= SelectedCheckBox_Click; 
      } 
     } 

    } 

    private void SelectedCheckBox_Click(object sender, RoutedEventArgs e) 
    { 
     _partSelectAllCheck.IsChecked = !ItemSourceList.AsQueryable().Any(x => x.Selected == false); 

    } 

    private void PartSelectAllCheckOnClick(object sender, RoutedEventArgs routedEventArgs) 
    { 
     foreach (CheckBox cb in VisualTreeHelpers.FindVisualChildren<CheckBox>(_partItemsListView)) 
     { 
      BindingExpression binding = cb.GetBindingExpression(CheckBox.IsCheckedProperty); 
      // ReSharper disable once PossibleNullReferenceException 
      if (binding.ParentBinding.Path.Path == "Selected") 
      { 
       cb.IsChecked = _partSelectAllCheck.IsChecked ?? false; 
      } 
     } 
    } 

} 

}

可能有人请张贴一些代码,显示了如何集合 - 创建默认模板?

回答

0

结果比我想象的要简单。由于ItemTemplate绑定到依赖项属性,我可以在其中指定默认模板。这只是留下了模板的创建。见下文。

#region ItemTemplate 

    public DataTemplate ItemTemplate 
    { 
     get { return (DataTemplate)GetValue(ItemTemplateProperty); } 
     set { SetValue(ItemTemplateProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ItemTemplateProperty = 
     DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(SelectItemsControl), 
     new UIPropertyMetadata(DefaultItemTemplate)); 

    private static DataTemplate DefaultItemTemplate 
    { 
     get 
     { 
      // tried using a MemoryStream - StreamWriter but was getting a 
      // "Root element missing error", would be nice to know why. 
      var sb = new StringBuilder(); 

      sb.Append("<DataTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">"); 
      sb.Append("<StackPanel Orientation=\"Horizontal\">"); 
      sb.Append("<CheckBox Margin=\"2\" IsChecked=\"{Binding Selected}\" />"); 
      sb.Append("<TextBlock Margin=\"5,2\" Text=\"{Binding Description}\" VerticalAlignment=\"Center\"/>"); 
      sb.Append("</StackPanel>"); 
      sb.Append("</DataTemplate>"); 

      var myByteArray = System.Text.Encoding.UTF8.GetBytes(sb.ToString()); 
      var ms = new MemoryStream(myByteArray); 

      return (DataTemplate) XamlReader.Load(ms); 
     } 
    } 

    #endregion