سؤال

My goal is to have a single Size property be updated by two different text boxes. Specifically with each component connected to a single textbox, which is highlighted in the XAML below. Specifically the line that says: {Binding Path=Dimensions.Width, ....}

private Size _dimension;
    public Size Dimensions
    {
        get { return _dimension; }
        set
        {
            _dimension = value;
            OnPropertyChanged("Dimensions");
        }
    }

My XAML is as follows:

<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
   <Label Content="Width" />
   <TextBox Width="50" Text="{Binding Path=Dimensions.Width, 
            Converter={StaticResource myStringToDoubleConverter},
            UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
   <Label Content="Height" />
   <TextBox Width="50" Text="{Binding Path=Dimensions.Height,
            Converter={StaticResource myStringToDoubleConverter},
            UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</StackPanel>

The issue I have is the set function is not called when I change the text in the textbox. I imagine the reason is because I am not supplying a Size object, but at best a double, and at worst a string. I have a converter in there to convert the string to double but it does not seem to be the trick. My feeling is Multibinding yields my solution however, all the examples I have seen is combining two strings into a third control. I am not interested in making a control with appended values but rather updating the components of my "complex" property.

I understand I can make two "sub" properties, a Height and Width, and then update the Dimension property a piece at a time, but it seems a more elegant solution should exist.

Why doesn't my textbox be properly bound to a single component of the Dimension property and update the dimension accordingly?

It seems excessive to construct a wrapper class that implements INotifyPropertyChanged.

public class MySizeClass : Size, INotifyPropertyChanged
هل كانت مفيدة؟

المحلول

If the Size type you're using here is the one provided by WPF, i.e. System.Windows.Size, then the problem is that it's not a class. It's a value type (i.e. a struct). And although it does provide setters for its Width and Height, it probably shouldn't because they don't do what you might expect.

C# won't let you modify Dimensions.Width either - if you write the following:

src.Dimensions.Width = 123;

where src is an instance of the type that contains those property definitions you've shown in your "Component Bind Solution", you'll get this error:

Cannot modify the return value of 'WpfApplication1.Source.Dimensions' because it is not a variable

C# is refusing here because of the same basic issue that stops this from working in WPF: it's because Size is a value type. Any attempt to retrieve the Dimensions property will return a whole new copy of the value.

Of course, that C# code is effectively short for:

Size dimensions = src.Dimensions;
dimensions.Width = 123;

C# will actually let you write this, but if you try it, you'll find why it stopped you writing the shorter version: src.Dimensions.Width will still return the old value. (What this code does is takes a copy of src.Dimensions, stores that copy in a local variable called dimensions, and then modifies that local variable, leaving the original unaltered.)

Basically, there's no way to get a reference to the Size instance that is the one returned by your Dimensions property - you can only ever get hold of a copy of the value. And the only way to update it is to write a complete new Size in its place. That's why you can't modify the Width or Height on its own.

Trying to fix this with converters isn't going to work, because the problem is not the value that you're trying to write into the target. The problem is that you want to write to a target that's inaccessible, i.e., a particular property of a value type. A binding can only write to a target property if it's a property of an object, not a value.

(In principle, Microsoft could have made WPF detect this, have it read the Dimensions property, modify the copy of the value it gets and then write that modified value back into Dimensions. And although that would have been convenient in this case, it would also be a significant change from how data binding normally deals with updates to properties. I think it's probably better that they didn't try, because it could easily mask other problems.)

In C#, you can write to individual properties when you've got a local variable or field containing a Size value of course, because the variable or field denotes a particular instance of the value. Fields, local variables, ref or out arguments, and array elements are special because it is in fact possible to work with the values they hold in situ. But you cannot do this when going through properties. It's the same reason that if you define a List<Size> you can't write myList[0].Width = 123; even though that's just fine with an array.

In any case, mutable value types are problematic for all sorts of reasons, not just this. They are generally frowned upon, and it's unfortunate that WPF ships a few.

If WPF were able to bind to fields, you'd be able to do what you want. But it isn't, so you can't.

نصائح أخرى

Here is the Component Bind Solution which pleasantly does work completely fine.

    private Double m_Width;
    public Double Width
    {
        get { return (Dimensions.Width); }
        set
        {
            m_Width = value;
            Dimensions= new Size(m_Width, m_Height);
        }
    }
    private Double m_Height;
    public Double Height
    {
        get { return (Dimensions.Height); }
        set
        {
            m_Height = value;
            Dimensions= new Size(m_Width, m_Height);
        }
    }
    private Size _dimension;
    public Size Dimensions
    {
        get { return _dimension; }
        set
        {
            _dimension = value;
            OnPropertyChanged("Dimensions");
        }
    }

Just seems that binding directly to Dimensions.Width is something that would be supported by WPF.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top