코드에서 수행하는 방법을 알고 있지만 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;
}
}
}
답변
다음 중 하나가 없으면 XAML에있을 수 없습니다.
- 숨겨진 컨트롤 만들기 (Alan Hunford의 답변)
- ControlTemplate을 대폭 변경합니다. 이 경우에도 ItemsPresenter의 숨겨진 버전을 만들어야 할 수 있습니다.
그 이유는 내가 본 기본 ComboBox ControlTemplate (Aero, Luna 등)이 모두 ItemsPresenter를 Popup에 중첩하기 때문입니다. 즉, 이러한 항목의 레이아웃이 실제로 표시 될 때까지 연기됩니다.
이를 테스트하는 쉬운 방법은 가장 바깥 쪽 컨테이너의 MinWidth (Aero와 Luna 모두에 대한 Grid)를 PART_Popup의 ActualWidth에 바인딩하도록 기본 ControlTemplate을 수정하는 것입니다. 드롭 버튼을 클릭하면 ComboBox가 너비를 자동으로 동기화 할 수 있지만 이전에는 동기화 할 수 없습니다.
당신이 (당신이 레이아웃 시스템의 측정 작업을 강제하지 않는 그래서 수있는 제 2 제어를 추가 할을), 나는 그것을 할 수 있다고 생각하지 않습니다.
항상 그렇듯이 짧고 우아한 솔루션에 열려 있지만이 경우에는 코드 숨김 또는 이중 제어 / ControlTemplate 해킹이 내가 본 유일한 솔루션입니다.
답변
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라는 ComboBox에 대한 확장 메서드를 호출하는 것입니다.이 메서드는 (보이지 않게) 자체적으로 확장 및 축소 된 다음 생성 된 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();
코드 숨김 (예 : ComboBox.Loaded 이벤트)
답변
네, 이건 좀 더럽습니다.
과거에 내가 한 일은 모든 항목을 동시에 표시하지만 가시성을 숨김으로 설정 한 숨겨진 목록 상자 (itemcontainerpanel이 그리드로 설정 됨)를 ControlTemplate에 추가하는 것입니다.
끔찍한 코드 숨김에 의존하지 않는 더 나은 아이디어 나 시각 자료를 지원하기 위해 너비를 제공하기 위해 다른 컨트롤을 사용해야한다는 것을 이해해야하는 귀하의 관점에 대해 듣게되어 기쁩니다.
답변
위의 다른 답변을 바탕으로 내 버전은 다음과 같습니다.
<Grid HorizontalAlignment="Left">
<ItemsControl ItemsSource="{Binding EnumValues}" Height="0" Margin="15,0"/>
<ComboBox ItemsSource="{Binding EnumValues}" />
</Grid>
HorizontalAlignment = “Left”는 포함하는 컨트롤의 전체 너비를 사용하여 컨트롤을 중지합니다. Height = “0”은 항목 컨트롤을 숨 깁니다.
Margin = “15,0”은 콤보 상자 항목 주위에 추가 크롬을 허용합니다 (크롬 불가지론이 아닙니다).
답변
필자는 이전 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>
물론 Grid의 SharedSizeScope 기능과 유사한 크기로 함께 크기를 조정하려는 경우가 아니라면 각 콤보 상자에 대해 별도의 GrowConverter 인스턴스가 필요합니다.
답변
Maleak의 답변에 대한 후속 조치 : 그 구현이 너무 마음에 들었고 이에 대한 실제 동작을 작성했습니다. System.Windows.Interactivity를 참조 할 수 있도록 Blend SDK가 필요합니다.
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();
}
}
}
답변
보관 용 계정 뒤에 동일한 콘텐츠가 포함 된 목록 상자를 놓습니다. 그런 다음 다음과 같은 바인딩으로 올바른 높이를 적용하십시오.
<Grid>
<ListBox x:Name="listBox" Height="{Binding ElementName=dropBox, Path=DesiredSize.Height}" />
<ComboBox x:Name="dropBox" />
</Grid>
