Giffyguy
Giffyguy

Reputation: 21292

How can I calculate the logical width of WPF visual element?

I need to calculate the logical width of a visual element, before it gets rendered by WPF.

For simplicity of explanation, I'll say that this visual element will likely be a Polygon object. It could be something else, but a Polygon makes it easy to visualize.

So the XAML might look something like this:

<Window x:Class="MyCLRNamespace.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Window>

And the code-behind might look something like this:

namespace MyCLRNamespace
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            //This is the visual element in question. It's a simple triangle.
            Polygon MyPolygon = new Polygon();
            MyPolygon.Points = new PointCollection {    new Point(100, 0),
                                                        new Point(200, 200),
                                                        new Point(0, 200)   };
            double PolyWidth = MyPolygon.Width;
            /* In this case, PolyWidth will be set to double.NaN, since
               MyPolygon.Width is never set.

               I need to be able to calculate the logical width of an element,
               unrelated to the WPF rendering system. This means that I can't
               rely on FrameworkElement.ActualWidth to calculate the width for
               me. I need to be able to look at the MyPolygon object (or its
               content) and figure out that it is set to a visual element that
               should be 200dips wide before any parent element applies any
               operations to it - regardless of what MyPolygon.Width may or may
               not be set to.

               It should also be noted that I don't have to rely on
               FrameorkElement. If there are more generic alternatives, such as
               the UIElement or Visual classes, I'd prefer to use those instead
               of the more specific FrameworkElement. The more robust I can make
               this, the better. */
        }
    }
}

Upvotes: 2

Views: 8240

Answers (2)

Giffyguy
Giffyguy

Reputation: 21292

The System.Windows.UIElement class provides methods for measuring itself outside of any parent-child element relationships.

It is critical that you check for IsMeasureValid before you attempt to use the measurement value(s). If IsMeasureValid is false, you need to manually call the UIElement.Measure() method to ensure that you have an up-to-date measurement the element and its content. If IsMeasureValid is true, it won't hurt to measure again. It will just overwrite any previous measurements it has stored.

If you want a solid measurement of the element with no outside restrictions, provide an infinite size as the availableSize parameter to the UIElement.Measure() method.

The UIElement.Measure() method will store the measured size of the element in the UIElement.DesiredSize property. I don't believe this has any negative impact on the WPF rendering system, due to the fact that any parent element is guaranteed to re-measure the element with its own available size constraints before rendering it. This may affect the final size of the element on screen, but it will not affect the original desired size of the element before parent-child constraints are applied.

namespace MyCLRNamespace
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            Polygon MyPolygon = new Polygon();
            MyPolygon.Points = new PointCollection {    new Point(100, 0),
                                                        new Point(200, 200),
                                                        new Point(0, 200)   };
            //if (MyPolygon.IsMeasureValid == false)
                MyPolygon.Measure(new Size( double.PositiveInfinity,
                                            double.PositiveInfinity));

            double PolyWidth = MyPolygon.DesiredSize.Width;
        }
    }
}

Upvotes: 11

Igor Zevaka
Igor Zevaka

Reputation: 76510

Alas we meet again. Perhaps it would help if you tell us more about what you are trying to achieve. WPF actually uses device independent units for its sizes and that's what ActualWidth is (from MSDN):

The element's width, as a value in device-independent units (1/96th inch per unit). The default value is 0 (zero).

If you are seeing weirdness in availability of ActualWidth values you might want to listen to SizeChanged event or override OnRenderSizeChanged. I think the two are subtly different, but I am not sure what those differences are.

Upvotes: 0

Related Questions