【问题标题】:Making a Viewbox scale vertically but stretch horizontally使 Viewbox 垂直缩放但水平拉伸
【发布时间】:2010-12-28 00:17:29
【问题描述】:

我想制作一个仅缩放其高度,然后水平拉伸其内容的 Viewbox(或类似的东西)。

如果我这样做:

<Viewbox>
  <StackPanel>
    <Button>Foo</Button>
    <Button>Bar</Button>
  </StackPanel>
</Viewbox>

然后我明白了:


(来源:excastle.com

它的作用就像两个按钮都有 Horizo​​ntalAlignment="Center",然后缩放结果。但我不想要 Horizo​​ntalAlignment="Center";我想要Horizo​​ntalAlignment="Stretch",像这样:


(来源:excastle.com

所以我希望它读取其内容所需的高度,仅根据高度计算缩放因子,然后让缩放后的内容水平拉伸。

有没有办法使用 Viewbox 和/或某些第三方面板来完成此操作?

【问题讨论】:

    标签: wpf viewbox


    【解决方案1】:

    WPF 中没有包含这样的控件,但您可以自己编写一个,而不会太麻烦。这是一个具有您的规范的自定义 ViewboxPanel:

    public class ViewboxPanel : Panel
    {
        private double scale;
    
        protected override Size MeasureOverride(Size availableSize)
        {
            double height = 0;
            Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
            foreach (UIElement child in Children)
            {
                child.Measure(unlimitedSize);
                height += child.DesiredSize.Height;
            }
            scale = availableSize.Height / height;
    
            return availableSize;
        }
    
        protected override Size ArrangeOverride(Size finalSize)
        {
            Transform scaleTransform = new ScaleTransform(scale, scale);
            double height = 0;
            foreach (UIElement child in Children)
            {
                child.RenderTransform = scaleTransform;
                child.Arrange(new Rect(new Point(0, scale * height), new Size(finalSize.Width / scale, child.DesiredSize.Height)));
                height += child.DesiredSize.Height;
            }
    
            return finalSize;
        }
    }
    

    然后你像这样使用它:

    <local:ViewboxPanel>
        <Button>Foo</Button>
        <Button>Bar</Button>
    </local:ViewboxPanel>
    

    它肯定需要一些工作,但这可能会让你开始。

    【讨论】:

    • 感谢您的出色解决方案。我在方法protected override Size MeasureOverride(Size availableSize) 中添加了if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) { availableSize = new Size(200, 200); },因此设计器可以正常工作而不会引发异常。
    【解决方案2】:

    保持宽度正常工作:

    protected override Size MeasureOverride(Size availableSize)
        {
            double height = 0;
            Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
            foreach (UIElement child in Children)
            {
                child.Measure(unlimitedSize);
                height += child.DesiredSize.Height;
            }
            scale = availableSize.Height / height;
    
            foreach (UIElement child in Children)
            {
                unlimitedSize.Width = availableSize.Width / scale;
                child.Measure(unlimitedSize);
            }           
    
            return availableSize;
        }
    

    【讨论】:

      【解决方案3】:

      我很确定答案是“不容易”。

      这是一个似乎可行但有点笨拙的想法:

      1. 将 StackPanel 放入 UserControl(下例中为 UserControl1)。

      2. 将此 UserControl 的两个副本放入一个容器(下例中为 Grid)。

        一个。将第一个副本顶部/左侧对齐,使其保持默认大小。此副本仅用于测量目的,应将其可见性设置为隐藏。

        b.将第二个副本放在 Viewbox 中。此副本是用户实际看到的副本。

      3. 使用 MultiBinding 和 MultiValueConverter 来调整第二个 UserControl 的宽度,使其在被 Viewbox 扩展之前具有正确的纵横比。

      这是标记:

      <Grid>
          <local:UserControl1 x:Name="RawControl" HorizontalAlignment="Left" VerticalAlignment="Top" Visibility="Hidden" />
          <Viewbox>
              <local:UserControl1>
                  <local:UserControl1.Width>
                      <MultiBinding Converter="{StaticResource WidthAdjuster}">
                          <Binding ElementName="RawControl" Path="ActualHeight" />
                          <Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualWidth" />
                          <Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualHeight" />
                      </MultiBinding>
                  </local:UserControl1.Width>
              </local:UserControl1>
          </Viewbox>
      </Grid>
      

      这里是 MultiValueConverter

      public class WidthAdjuster : IMultiValueConverter
      {
          public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
          {
              var rawHeight = (double)values[0];
              var containerWidth = (double)values[1];
              var containerHeight = (double)values[2];
              var ratio = containerWidth / containerHeight;
              return rawHeight * ratio;
          }
      
          public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
          {
              throw new NotImplementedException();
          }
      }
      

      525 x 350 容器的结果

      【讨论】:

        【解决方案4】:

        我遇到了几乎类似的问题。我的面板必须适合所有的孩子,将它们排成一排并拉伸它们,均匀地填充面板。上面的算法使用渲染变换来缩放元素。问题是渲染变换会拉伸孩子自己,但忽略边距。如果边距很高且比例系数低于 1,则元素会从面板中掉出。您必须根据该轴上的边距校正 RenderTransform 的比例,并使用相同的比例系数进行排列。我使用的 MeasureOverride 是

        protected override Size MeasureOverride(Size availableSize)
        {
            double width = 0; double maxHeight = 0; double mY=0; double mX=0;
            Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
            foreach (UIElement child in Children)
            {
                child.Measure(unlimitedSize);
                width += child.DesiredSize.Width;
                maxHeight = Math.Max(maxHeight, child.DesiredSize.Height);
        
                FrameworkElement cld = child as FrameworkElement;
                mY = cld.Margin.Top + cld.Margin.Bottom;
                mX = cld.Margin.Left + cld.Margin.Right;
            }
        
            double scaleX = availableSize.Width / width;
            double scaleY = availableSize.Height / maxHeight;
            //That is scale for arranging
            positionScaling = Math.Min(scaleX, scaleY); 
        
            try
            {
                // Let FrameworkElement hight be Xn. mY = Element.Margin.Top + Element.Margin.bottom.
                // DesiredSize includes margin therefore:
                // (Yn + mY) * scaleY = availableSize.Height
                // But render transform doesn't scales margin. Actual render height with margin will be
                // Yn * RenderScaleY + mY = availableSize.Height;
                // We must find render transform scale coeff like this:
        
                double yn = availableSize.Height / scaleY - mY;
                scaleY = (availableSize.Height - mY) / yn;
        
                double xn = availableSize.Width / scaleX - mX;
                scaleX = (availableSize.Width - mX) / xn;
        
        
                scale = Math.Min(scaleX, scaleY); //scale to use in RenderTransform   
                // In my project all children are similar in size and margin, algorithm BREAKS otherwise!!!
            }
            catch { scale = 1; }
        
            return availableSize;
        }
        

        再次重申:在我的项目中,所有孩子的大小和边距都相似,否则算法会中断。

        【讨论】:

          【解决方案5】:

          除了Rick Sladkey的回答,在所有元素的汇总高度变得太小并且水平方向出现尺寸过大的情况下,使元素在右侧被切割,这里是测量方法的一点修复:

          protected override Size MeasureOverride(Size availableSize)
              {
                  double height = 0;
                  Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
                  foreach (UIElement child in Children)
                  {
                      child.Measure(unlimitedSize);
                      height += child.DesiredSize.Height;
                  }
                  scale = availableSize.Height / height;
                  
                  foreach (UIElement child in Children)
                  {
                      var childDesireWidth = child.DesiredSize.Width;
                      if (childDesireWidth * scale > availableSize.Width)
                          scale = availableSize.Width / childDesireWidth;
                  }
          
                  return availableSize;
              }
          

          【讨论】:

            猜你喜欢
            • 2015-02-23
            • 2016-03-26
            • 2016-12-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多