2009-06-23 71 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

+0

どのように問題を解決しましたか? –

+0

http://stackoverflow.com/questions/826985/make-wpf-comboboxes-fill-a-whole-column-widthで同様の行の別の記事をチェックしてください。これがあなたの質問に答える場合は、あなたの質問に「回答済み」と記入してください。 – Sudeep

答えて

27

これはどちらかなしでXAMLにすることはできません。

  • は大幅のControlTemplateを変更し、隠しコントロール(アランHunfordの答え)
  • を作成します。この場合でも、ItemsPresenterの隠しバージョンを作成する必要があります。

これは、私が出会ったデフォルトのコンボボックスControlTemplates(Aero、Lunaなど)がPopup内のItemsPresenterをネストするためです。つまり、これらのアイテムのレイアウトは、実際に表示されるまで延期されます。

これを簡単にテストするには、デフォルトのControlTemplateを変更して、最も外側のコンテナ(AeroとLunaのグリッド)のMinWidthをPART_PopupのActualWidthにバインドします。ドロップボタンをクリックしたときにComboBoxの幅を自動的に同期させることはできますが、以前は使用できません。

したがって、レイアウトシステムで測定操作を強制できない限り(に2番目のコントロールを追加して実行できます)、私はそれを行うことはできないと思います。

いつものように、私は短くてエレガントなソリューションを公開していますが、コードビハインドやデュアルコントロール/ ControlTemplateのハッキングは私が見た唯一の解決策です。

10

うん、これは少し厄介です。

私が過去に行ったことは、ControlTemplateに、すべての項目を同時に表示するが、可視性を非表示にした非表示のリストボックス(itemscontainerpanelをグリッドに設定したもの)を追加することです。

恐ろしいコードビハインドや、ビジュアルをサポートするための幅を提供するために別のコントロールを使用する必要があることを理解する必要がない、優れたアイデアについてお聞きしたいと思います。 )。

+0

このアプローチは、選択項目のときに最も広い項目が完全に見えるようにコンボのサイズを十分に広げますか?これは私が問題を見たところです。 – jschroedl

+9

コードを表示できますか? –

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> 

あなたは私がIValueConverterまたはIMultiValueConverterをimpmentingになり書いたC#のをどうしようとしている正確に何を取得するには。

0

同じコンテンツを含むリストボックスをドロップボックスの後ろに置きます。そして、いくつかは次のように結合して、正しい高さを強制:

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

私は古いWinFormsのと同様に、この問題を「十分に良い」解決策は、コンボボックスは、それが開催された最大サイズより縮小することはありません作ることになってしまいましたAutoSizeMode = GrowOnly。

私は、これはカスタム値コンバーターとあったんでした方法:これであなたは別のインスタンスが必要であることを

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

注:

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でのコンボボックスを設定しますグリッドのSharedSizeScope機能と同様に、各コンボボックスのGrowConverterのサイズを合わせる必要がある場合を除き、

+0

ニースですが、最長のエントリを選択した後は「安定」のみです。 – primfaktor

+1

正しい。 WinFormsでは、テキストAPIを使用してコンボボックスのすべての文字列を測定し、それを考慮に入れて最小幅を設定するために、これについて何かしました。 WPFでは、特にアイテムが文字列ではなく、かつ/またはバインディングから来ている場合、これを行うことはかなり困難です。 – Cheetah

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に基づいてWidthを計算します。この拡張方法は、コンボボックス内のコードビハインドで

comboBox.SetWidthFromItems(); 

を呼び出す機能(例えばに提供そして

拡張メソッド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(); 
    } 
} 

(IExpandCollapseProviderはUIAutomationProvider.dllへの参照を必要とします)。ロードされたイベント)

+0

良い解決策、ありがとう。 –

+0

+1、素晴らしい解決策!私は同じ行に沿って何かをしようとしていましたが、結局私はあなたの実装を(いくつかの変更を加えて)使用しました –

+1

すばらしいおかげで。これは受け入れられた答えとしてマークされるべきです。接続されたプロパティのように常にすべての方法です:) –

0

私の場合は、もっと単純な方法でトリックをしたようです。 私はちょうどextrコンボボックスをラップするstackPanel

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

は、上記の他の回答に基づいて

+1

-1:これを試してみましたが、全く動作していません。 –

6

(のVisual Studio 2008で働いていた)、ここでは私のバージョンです:

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

たHorizo​​ntalAlignment = "左" の全幅を使用してコントロールを停止しますコントロールを含む。 高さ= "0"はアイテムコントロールを非表示にします。
Margin = "15,0"は、コンボボックスの項目の周囲にクロムを追加することができます(クロムに敬遠しないでください)。

2

マラクの答えへのフォローアップ:私はその実装がとても好きで、実際の行動を書いています。明らかに、Blend SDKが必要なので、System.Windows.Interactivityを参照できます。

XAML:

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

コード:私はすべてのUIElementがありUpdateLayout()方法に出くわしたときに私は、答えのために自分を探していた

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

ありがとうございました!

ItemSourceを設定または変更した後にComboBox1.Updatelayout();に電話するだけです。私として

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

これにより、コンボボックスに空き領域が確保されます。 OPには何も答えません – Assimilater

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

コンボボックスに幅を追加幅は一番広い要素までですが、コンボボックスを一度開いた後でなければなりません。

<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> 
関連する問題