2017-08-17 38 views
1

我在How can I make a WPF combo box have the width of its widest element in XAML?如何在EventHandler中處理錯誤的ComboBox.ItemContainerGenerator.Status?

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

使用組合框是得到它從附加的行爲答案(目前46 upvotes)中描述的最寬元素的寬度但是在win10動態縮放文字大小時,該解決方案不起作用。問題是,即使條件

comboBox.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated 

是真實的,呼籲

comboBox.ItemContainerGenerator.ContainerFromItem(item); 

收益從看似確定的項目爲空。

所以我的問題是:我應該如何改變寬度計算正確的代碼?我問這是因爲我沒有win10,無法複製和玩耍。我得請同事測試一下。

我試圖消除線

comboBox.ItemContainerGenerator.StatusChanged -= eventHandler; 

這導致正確的寬度被點擊鼠標窄組合框時被測量。所以一個答案是強制StatusChanged事件在某處以某種方式提出。

回答

0

我想出了一個解決方案,即遠離完美只有一個工作輪。測量需要一次完成,因爲在準備就緒後我不會將項目添加到ComboBox。所以我一直測量組合框的記錄:

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(項目)返回null我們不瑟寬度和不添加的組合框的名稱設置測量組合框的:

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