如何在EventHandler中处理错误的ComboBox.ItemContainerGenerator.Status?

时间:2017-08-17 08:30:16

标签: c# wpf combobox attachedbehaviors

我正在使用一个组合框,它从How can I make a WPF combo box have the width of its widest element in XAML?

中附加行为答案(目前有46个赞成票)中描述的最宽元素获取宽度
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)
            {
                 var container = comboBox.ItemContainerGenerator.ContainerFromItem(item);
                 if (container is ComboBoxItem)
                 {
                     var comboBoxItem = (ComboBoxItem) container;
                     comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                     if (comboBoxItem.DesiredSize.Width > width)
                     {
                         width = comboBoxItem.DesiredSize.Width;
                     }
                 }
                 else
                 {
                    /* FIXME: coming here means that for some reason ComboBoxItems */ 
                    /* are not generated even if comboBox.ItemContainerGenerator.Status seems to be OK  */
                    return;
                 }                
            }
            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();
}

但是,在win10中动态缩放文本大小时,解决方案不起作用。问题是即使条件

comboBox.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated

是真的,要求

comboBox.ItemContainerGenerator.ContainerFromItem(item); 

从看似正常的项目返回null。

所以我的问题是:我应该如何更改正确计算宽度的代码?我问这个是因为我没有win10而且无法重现和玩耍。我得请一位同事测试一下。

我尝试删除行

comboBox.ItemContainerGenerator.StatusChanged -= eventHandler;

这导致在用鼠标点击窄组合框时测量正确的宽度。所以一个答案就是强迫StatusChanged事件以某种方式在某处提升。

1 个答案:

答案 0 :(得分:0)

我想出了一个解决方案远非完美 只是一个工作轮。测量只需要进行一次,因为我准备好后不会向ComboBox添加项目。所以我记录了测量的ComboBoxes:

    private static HashSet<string> _measuredWidthNamesSet = new HashSet<string>();
    private static void OnComboBoxLoaded(object sender, RoutedEventArgs e)
    {
        ComboBox comboBox = sender as ComboBox;
        if (!_measuredWidthNamesSet.Contains(comboBox.Name))
        {
            Action action = () => { comboBox.SetWidthFromItems(_measuredWidthNamesSet); };
            comboBox.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
        }
    }

如果comboBox.ItemContainerGenerator.ContainerFromItem(item)返回null,我们不会显示宽度,也不会将ComboBox名称添加到测量的ComboBoxes集合中:

    public static void SetWidthFromItems(this ComboBox comboBox, HashSet<string> measuredWidthNamesSet)
    {
        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)
            {
                bool isSuccess = true;
                double width = 0;
                foreach (var item in comboBox.Items)
                {
                    var container = comboBox.ItemContainerGenerator.ContainerFromItem(item);
                    if (container is ComboBoxItem)
                    {
                        var comboBoxItem = (ComboBoxItem) container;
                        comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                        if (comboBoxItem.DesiredSize.Width > width)
                        {
                            width = comboBoxItem.DesiredSize.Width;
                        }
                    }
                    else
                    {
                        /* coming here means that for some reason ComboBoxItems are not generated even if
                         * comboBox.ItemContainerGenerator.Status seems to be OK */
                        isSuccess = false;
                        break;
                    }
                }
                if (isSuccess)
                {
                    comboBox.Width = comboBoxWidth + width;
                    measuredWidthNamesSet.Add(comboBox.Name);
                }

                // 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();
    }