2009-06-23 38 views
82

我知道如何在代碼中做到這一點,但這可以在XAML中完成嗎?如何讓WPF組合框具有XAML中最寬元素的寬度?

Window1.xaml:

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300"> 
    <Grid> 
     <ComboBox Name="ComboBox1" HorizontalAlignment="Left" VerticalAlignment="Top"> 
      <ComboBoxItem>ComboBoxItem1</ComboBoxItem> 
      <ComboBoxItem>ComboBoxItem2</ComboBoxItem> 
     </ComboBox> 
    </Grid> 
</Window> 

Window1.xaml.cs:

using System.Windows; 
using System.Windows.Controls; 

namespace WpfApplication1 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
      double width = 0; 
      foreach (ComboBoxItem item in ComboBox1.Items) 
      { 
       item.Measure(new Size(
        double.PositiveInfinity, double.PositiveInfinity)); 
       if (item.DesiredSize.Width > width) 
        width = item.DesiredSize.Width; 
      } 
      ComboBox1.Measure(new Size(
       double.PositiveInfinity, double.PositiveInfinity)); 
      ComboBox1.Width = ComboBox1.DesiredSize.Width + width; 
     } 
    } 
} 
+0

我已經在代碼中嘗試了這種方法,但發現測量可以在Vista和XP之間有所不同。在Vista上,DesiredSize通常包含下拉箭頭大小,但在XP上,寬度通常不包括下拉箭頭。現在,我的結果可能是因爲我試圖在父窗口可見之前進行測量。在Measure之前添加UpdateLayout()可以提供幫助,但可能會在應用程序中導致其他副作用。 如果你願意分享,我會很高興看到你提出的解決方案。 – jschroedl 2009-08-09 17:08:09

+0

你是如何解決你的問題的? – 2010-10-25 16:51:06

+0

請查看http://stackoverflow.com/questions/826985/make-wpf-comboboxes-fill-a-whole-column-width上類似行的另一篇文章請將您的問題標記爲「回答」,如果這回答您的問題。 – Sudeep 2009-06-23 19:36:52

回答

27

這不可能是在XAML沒有之一:

  • 創建一個隱藏的控制(阿蘭Hunford的答案)
  • 改變的ControlTemplate顯着。即使在這種情況下,也可能需要創建ItemsPresenter的隱藏版本。

原因是我碰到的默認ComboBox ControlTemplates(Aero,Luna等)都將ItemsPresenter嵌套在Popup中。這意味着這些項目的佈局被推遲到實際可見時。

測試此方法的一種簡單方法是修改默認的ControlTemplate,將最外層容器的MinWidth(它是Aero和Luna的網格)綁定到PART_Popup的ActualWidth。當您單擊放置按鈕時,您將能夠使ComboBox自動同步它的寬度,但不會在之前。

所以,除非你可以強制佈局系統(你可以通過添加第二個控制做的)一個測量操作,我不認爲這是可以做到。

與往常一樣,我打開一個簡短而優雅的解決方案 - 但在這種情況下,我看到了一個代碼隱藏或雙控制/ ControlTemplate黑客攻擊。

10

呀,這個人是有點討厭。

我已經在過去做什麼是添加到ControlTemplate中一個隱藏列表框(其itemscontainerpanel設置爲一個網格)顯示在同一時間但其可見性設置爲隱藏的每一個項目。

我很高興聽到任何不依賴於可怕的代碼隱藏的更好的想法,或者您的視圖必須理解它需要使用不同的控件來提供寬度來支持視覺效果(yuck! )。

+0

此方法是否會將組合的尺寸調整到足夠寬,以便最寬的項目在選定項目時完全可見?這是我見過問題的地方。 – jschroedl 2009-08-09 17:11:15

+9

你能否顯示你的代碼。 – 2010-10-26 15:00:28

0

您可以綁定任何你想要的容器寬度。

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300" x:Name="Window1"> 
<Grid> 
    <ComboBox 
     Name="ComboBox1" 
     HorizontalAlignment="Left" 
     VerticalAlignment="Top"> 
     <ComboBox.Width> 
      <Binding ElementName="Window1" Path="ActualWidth"/> 
     </ComboBox.Width> 
      <ComboBoxItem>ComboBoxItem1</ComboBoxItem> 
      <ComboBoxItem>ComboBoxItem2</ComboBoxItem> 
    </ComboBox> 
</Grid> 

要獲得正是你正在嘗試與你寫我想看看impmenting一個的IValueConverter或IMultiValueConverter的C#做。

0

將一個包含相同內容的列表框放在保管箱後面。然後執行正確的高度有一些像這樣的結合:

<Grid> 
     <ListBox x:Name="listBox" Height="{Binding ElementName=dropBox, Path=DesiredSize.Height}" /> 
     <ComboBox x:Name="dropBox" /> 
</Grid> 
4

我結束了一個「足夠好」的解決這個問題是使組合框不會縮小其下舉辦的最大規模的大小,類似於舊的WinForms AutoSizeMode = GrowOnly。

我這樣做是有一個自定義值轉換器的方式:

public class GrowConverter : IValueConverter 
{ 
    public double Minimum 
    { 
     get; 
     set; 
    } 

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     var dvalue = (double)value; 
     if (dvalue > Minimum) 
      Minimum = dvalue; 
     else if (dvalue < Minimum) 
      dvalue = Minimum; 
     return dvalue; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     throw new NotSupportedException(); 
    } 
} 

然後我配置組合框在XAML像這樣:

<Whatever> 
     <Whatever.Resources> 
      <my:GrowConverter x:Key="grow" /> 
     </Whatever.Resources> 
     ... 
     <ComboBox MinWidth="{Binding ActualWidth,RelativeSource={RelativeSource Self},Converter={StaticResource grow}}" /> 
    </Whatever> 

請注意,這個你需要一個單獨的實例每個組合框的GrowConverter,除非你想要一組尺寸放在一起,類似於Grid的SharedSizeScope功能。

+0

很好,但只有在選擇最長條目後才「穩定」。 – primfaktor 2012-06-29 06:44:12

50

您不能直接在Xaml中執行此操作,但可以使用此附加行爲。 (寬度將在設計中可見)

<ComboBox behaviors:ComboBoxWidthFromItemsBehavior.ComboBoxWidthFromItems="True"> 
    <ComboBoxItem Content="Short"/> 
    <ComboBoxItem Content="Medium Long"/> 
    <ComboBoxItem Content="Min"/> 
</ComboBox> 

的附加行爲ComboBoxWidthFromItemsProperty

public static class ComboBoxWidthFromItemsBehavior 
{ 
    public static readonly DependencyProperty ComboBoxWidthFromItemsProperty = 
     DependencyProperty.RegisterAttached 
     (
      "ComboBoxWidthFromItems", 
      typeof(bool), 
      typeof(ComboBoxWidthFromItemsBehavior), 
      new UIPropertyMetadata(false, OnComboBoxWidthFromItemsPropertyChanged) 
     ); 
    public static bool GetComboBoxWidthFromItems(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(ComboBoxWidthFromItemsProperty); 
    } 
    public static void SetComboBoxWidthFromItems(DependencyObject obj, bool value) 
    { 
     obj.SetValue(ComboBoxWidthFromItemsProperty, value); 
    } 
    private static void OnComboBoxWidthFromItemsPropertyChanged(DependencyObject dpo, 
                   DependencyPropertyChangedEventArgs e) 
    { 
     ComboBox comboBox = dpo as ComboBox; 
     if (comboBox != null) 
     { 
      if ((bool)e.NewValue == true) 
      { 
       comboBox.Loaded += OnComboBoxLoaded; 
      } 
      else 
      { 
       comboBox.Loaded -= OnComboBoxLoaded; 
      } 
     } 
    } 
    private static void OnComboBoxLoaded(object sender, RoutedEventArgs e) 
    { 
     ComboBox comboBox = sender as ComboBox; 
     Action action =() => { comboBox.SetWidthFromItems(); }; 
     comboBox.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle); 
    } 
} 

它所做的是,它要求組合框稱爲SetWidthFromItems的擴展方法,(無形)展開和摺疊本身然後根據生成的ComboBoxItems計算寬度。 (IExpandCollapseProvider要求UIAutomationProvider.dll的引用)

然後擴展方法SetWidthFromItems

public static class ComboBoxExtensionMethods 
{ 
    public static void SetWidthFromItems(this ComboBox comboBox) 
    { 
     double comboBoxWidth = 19;// comboBox.DesiredSize.Width; 

     // Create the peer and provider to expand the comboBox in code behind. 
     ComboBoxAutomationPeer peer = new ComboBoxAutomationPeer(comboBox); 
     IExpandCollapseProvider provider = (IExpandCollapseProvider)peer.GetPattern(PatternInterface.ExpandCollapse); 
     EventHandler eventHandler = null; 
     eventHandler = new EventHandler(delegate 
     { 
      if (comboBox.IsDropDownOpen && 
       comboBox.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
      { 
       double width = 0; 
       foreach (var item in comboBox.Items) 
       { 
        ComboBoxItem comboBoxItem = comboBox.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem; 
        comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 
        if (comboBoxItem.DesiredSize.Width > width) 
        { 
         width = comboBoxItem.DesiredSize.Width; 
        } 
       } 
       comboBox.Width = comboBoxWidth + width; 
       // Remove the event handler. 
       comboBox.ItemContainerGenerator.StatusChanged -= eventHandler; 
       comboBox.DropDownOpened -= eventHandler; 
       provider.Collapse(); 
      } 
     }); 
     comboBox.ItemContainerGenerator.StatusChanged += eventHandler; 
     comboBox.DropDownOpened += eventHandler; 
     // Expand the comboBox to generate all its ComboBoxItem's. 
     provider.Expand(); 
    } 
} 

這個擴展方法也提供了呼叫

comboBox.SetWidthFromItems(); 
在代碼

後面能力(例如,在組合框。加載事件)

0

在我的情況下,一個簡單得多的方法似乎是伎倆, 我剛剛使用了一個extr一個用於包裝組合框的stackPanel。

<StackPanel Grid.Row="1" Orientation="Horizontal"> 
    <ComboBox ItemsSource="{Binding ExecutionTimesModeList}" Width="Auto" 
     SelectedValuePath="Item" DisplayMemberPath="FriendlyName" 
     SelectedValue="{Binding Model.SelectedExecutionTimesMode}" />  
</StackPanel> 

6

基於上述其他答案(在Visual Studio 2008的工作),這裏是我的版本:

<Grid HorizontalAlignment="Left"> 
    <ItemsControl ItemsSource="{Binding EnumValues}" Height="0" Margin="15,0"/> 
    <ComboBox ItemsSource="{Binding EnumValues}" /> 
</Grid> 

的Horizo​​ntalAlignment = 「左」 停止使用的整個寬度的控制含有控制。 Height =「0」隱藏物品控制。
保證金=「15,0」允許在組合框項目周圍額外鍍鉻(我不害怕鍍鉻不可知)。

2

跟隨馬列克的答案:我非常喜歡這個實現,我爲它寫了一個實際的行爲。顯然你需要Blend SDK,以便引用System.Windows.Interactivity。

XAML:

<ComboBox ItemsSource="{Binding ListOfStuff}"> 
     <i:Interaction.Behaviors> 
      <local:ComboBoxWidthBehavior /> 
     </i:Interaction.Behaviors> 
    </ComboBox> 

代碼:

using System; 
using System.Windows; 
using System.Windows.Automation.Peers; 
using System.Windows.Automation.Provider; 
using System.Windows.Controls; 
using System.Windows.Controls.Primitives; 
using System.Windows.Interactivity; 

namespace MyLibrary 
{ 
    public class ComboBoxWidthBehavior : Behavior<ComboBox> 
    { 
     protected override void OnAttached() 
     { 
      base.OnAttached(); 
      AssociatedObject.Loaded += OnLoaded; 
     } 

     protected override void OnDetaching() 
     { 
      base.OnDetaching(); 
      AssociatedObject.Loaded -= OnLoaded; 
     } 

     private void OnLoaded(object sender, RoutedEventArgs e) 
     { 
      var desiredWidth = AssociatedObject.DesiredSize.Width; 

      // Create the peer and provider to expand the comboBox in code behind. 
      var peer = new ComboBoxAutomationPeer(AssociatedObject); 
      var provider = peer.GetPattern(PatternInterface.ExpandCollapse) as IExpandCollapseProvider; 
      if (provider == null) 
       return; 

      EventHandler[] handler = {null}; // array usage prevents access to modified closure 
      handler[0] = new EventHandler(delegate 
      { 
       if (!AssociatedObject.IsDropDownOpen || AssociatedObject.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) 
        return; 

       double largestWidth = 0; 
       foreach (var item in AssociatedObject.Items) 
       { 
        var comboBoxItem = AssociatedObject.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem; 
        if (comboBoxItem == null) 
         continue; 

        comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 
        if (comboBoxItem.DesiredSize.Width > largestWidth) 
         largestWidth = comboBoxItem.DesiredSize.Width; 
       } 

       AssociatedObject.Width = desiredWidth + largestWidth; 

       // Remove the event handler. 
       AssociatedObject.ItemContainerGenerator.StatusChanged -= handler[0]; 
       AssociatedObject.DropDownOpened -= handler[0]; 
       provider.Collapse(); 
      }); 

      AssociatedObject.ItemContainerGenerator.StatusChanged += handler[0]; 
      AssociatedObject.DropDownOpened += handler[0]; 

      // Expand the comboBox to generate all its ComboBoxItem's. 
      provider.Expand(); 
     } 
    } 
} 
0

我一直在尋找自己的答案,當我遇到每一個UIElementUpdateLayout()方法來。

現在很簡單,謝天謝地!

只要打電話ComboBox1.Updatelayout();設置後或修改ItemSource

0

至於我,擴大ComboBox.Width整個列寬的解決方案,是設置ColumnDefinition寬度爲「*」,而不是「自動」:

<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="140" /> 
     <ColumnDefinition Width="*" /> 
    </Grid.ColumnDefinitions> 

    <Label Content="List of items" 
     Grid.Column="0" Margin="3" /> 

    <ComboBox 
     Grid.Column="1" 
     ItemsSource="{Binding Path=DestinationSubDivisions}" 
     SelectedValue="{Binding Path=TransferRequest.DestinationSubDivision}" 
     DisplayMemberPath="Name" 
     Margin="3" /> 
</Grid> 
0

阿倫哈福德的做法,在實踐中:

<Grid> 

    <Grid.ColumnDefinitions> 
    <ColumnDefinition Width="Auto"/> 
    <ColumnDefinition Width="*"/> 
    </Grid.ColumnDefinitions> 

    <!-- hidden listbox that has all the items in one grid --> 
    <ListBox ItemsSource="{Binding Items, ElementName=uiComboBox, Mode=OneWay}" Height="10" VerticalAlignment="Top" Visibility="Hidden"> 
    <ListBox.ItemsPanel><ItemsPanelTemplate><Grid/></ItemsPanelTemplate></ListBox.ItemsPanel> 
    </ListBox> 

    <ComboBox VerticalAlignment="Top" SelectedIndex="0" x:Name="uiComboBox"> 
    <ComboBoxItem>foo</ComboBoxItem> 
    <ComboBoxItem>bar</ComboBoxItem> 
    <ComboBoxItem>fiuafiouhoiruhslkfhalsjfhalhflasdkf</ComboBoxItem> 
    </ComboBox> 

</Grid> 
0

只是寬度增加了組合框

<ComboBox Name="ComboBox1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100"> 
0

這將保持在W寬度最寬的元素,但只能打開組合框一次。

<ComboBox ItemsSource="{Binding ComboBoxItems}" Grid.IsSharedSizeScope="True" HorizontalAlignment="Left"> 
    <ComboBox.ItemTemplate> 
     <DataTemplate> 
      <Grid> 
       <Grid.ColumnDefinitions> 
        <ColumnDefinition SharedSizeGroup="sharedSizeGroup"/> 
       </Grid.ColumnDefinitions> 
       <TextBlock Text="{Binding}"/> 
      </Grid> 
     </DataTemplate> 
    </ComboBox.ItemTemplate> 
</ComboBox> 
相關問題