0

I have a Canvas inside an ItemsControl whose ItemsSource is an ObservableCollection. Withing this is a Rectangle whose Width property is bound to RectangleOverlay's wWdith property. Width appears 0 no matter what I have tried & I have tried moving things around. Here is the View.

<Grid>
        <Label  HorizontalAlignment="Center" 
                Content="IMAGE FROM CAMERA" 
                Width="Auto" FontFamily="White"
                Canvas.Left="50" Panel.ZIndex="2"/>
        <ItemsControl Panel.ZIndex="3" 
                   ItemsSource="{Binding Source=MyProperty}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Canvas Height="Auto" >
                    <Rectangle Width="{Binding Path=wWidth,
                                       UpdateSourceTrigger=PropertyChanged}"
                           Height="50"
                           Canvas.Left="50"
                           Canvas.Top="50"                                   
                           Stroke="Red"
                           StrokeThickness="2"/>
                    <TextBox Canvas.Left="50"
                         Canvas.Top="100"
                         Text="TEXT"/>
                </Canvas>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

</Grid>

And here is the code for RectangleOverlay class

public class RectOverlay : Shape, INotifyPropertyChanged
{
    public RectOverlay()
    {

    }

   //I DONT KNOW WHAT EXACTLY NEEDS TO BE IN HERE 
    protected override Geometry DefiningGeometry
    {
        get { return new RectangleGeometry();}
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    private double _width;
    public double wWidth
    {
        get { return _width; }
        set
        {
            _width = value;
            OnPropertyChanged("wWidth");
        }
    }


}

And here is most of the code behind(MainWindow.xaml.cs).

 public partial class MainWindow : Window, INotifyPropertyChanged
{

    private ObservableCollection<RectOverlay> ListShapes;


    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = this;

        MyProperty = new ObservableCollection<RectOverlay>();

        ListShapes.Add(new RectOverlay() { wWidth = 100 });

        _timer = new Timer(2000); // Set up the timer for 3 seconds
        _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
        _timer.Enabled = true; // Enable it
    }

    public ObservableCollection<RectOverlay> MyProperty
    {
        get { return ListShapes; }
        set
        {
            ListShapes = value;
            OnPropertyChanged("MyProperty");
        }
    }


}

I cannot seem to understanding why the binding fails.

System.Windows.Data Error: 40 : BindingExpression path error: 'wWidth' property not found on 'object' ''Char' (HashCode=5046349)'. BindingExpression:Path=wWidth; DataItem='Char' (HashCode=5046349); target element is 'Rectangle' (Name=''); target property is 'Width' (type 'Double')

I have read & tried to figure out from other similar posts on SO about how to infer this error. I think I have the binding flow correctly set. The data context of the entire window is the code behind, there after ItemsControl Source is the ObservableCollection of a custom class(RectangleOverlay). & inside Data Template, I am setting path to a property of my Custom class.

Clemens
  • 123,504
  • 12
  • 155
  • 268
  • I don't understand what all this is supposed to do. As a note, setting `UpdateSourceTrigger=PropertyChanged` on the Rectangle's Width Binding is entirely pointless. And why in the world is RectOverlay derived from Shape? Shape is a UI element, which does not belong to a view model, besides that you never actually show it anywhere. – Clemens May 07 '18 at 21:06
  • ultimately have an ObservableCollection of Rectangle shapes(overlays) whose xpos & ypos will keep changing. Since default Shape class does not have x & y, I have a custom Rectangle class where I will introduce X & Y. But for this example, just trying to implement collection with 1 Rectangle whose width is set to 100 & so hoping to see a Red rectangle of W=50 & H=50 at listed X,Y positions(Canvas.Left & Canvas.Top) – ItsTheJourney May 07 '18 at 21:14
  • Take a look at [this answer](https://stackoverflow.com/a/22325266/1136211) for how to show Rectangles in an ItemsControl using MVVM. It makes absolutely no sense to derive from Shape in a view model. What you may use is class Geometry and classes derived from it like RectangleGeometry. – Clemens May 07 '18 at 21:15
  • As shown here: https://stackoverflow.com/a/40720205/1136211 – Clemens May 07 '18 at 21:22

1 Answers1

0

The first error is that you wrote

ItemsSource="{Binding Source=MyProperty}"

instead of

ItemsSource="{Binding Path=MyProperty}"

or just

ItemsSource="{Binding MyProperty}"

Setting the Bindings's Source object to MyProperty just assigns the string literal "MyProperty", which is an IEnumerable of Chars and hence interpreted as such.

The Binding in the ItemTemplate then inevitably complains that it couldn't find a wWidth property on an object of type Char.


But there is another problem you are facing. Here's the correct answer and to be honest I had to dig to get this for you... I've never had this problem before.

First, since you are using a custom shape the ItemsControl is ignoring the custom DataTemplate and attempting to draw the RectOverlay that you've provided.

Now of course you're not seeing this because you're setting only the wWidth of it, which isn't a real drawing width. You need to set the Width, Height, Stroke, StrokeThickness etc to your custom shape and then you need to remove the DataTemplate completely from your XAML.

However, once you do that you'll still notice it doesn't draw. This is because your DefiningGeometry is returning a blank RectangleGeometry. This needs to be the paths etc that you intend to draw. (More on that if needed later.)

All that said, I truly don't feel that you need a custom shape here; if you do please give the full requirements and I'll provide a working custom shape and show have that working. I did it on my machine here but I'm not going to post that code because I think it's not what you really need.

Another thing to note... If you are using a custom shape it is from type DependencyObject. This means that implementing INotifyPropertyChanged is overkill. You can just use a custom DependencyProperty instead.

So please review this code below. I modified the RectOverlay to be more of a ViewModel and I update the MyProperty in the MainWindow to be a DependencyProperty.

Here's the MainWindow code behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        MyProperty.Add(new RectOverlay() { wWidth = 100 });
    }

    public ObservableCollection<RectOverlay> MyProperty
    {
        get { return (ObservableCollection<RectOverlay>)GetValue(MyPropertyProperty); }
        set { SetValue(MyPropertyProperty, value); }
    }

    public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.Register(nameof(MyProperty), typeof(ObservableCollection<RectOverlay>), typeof(MainWindow), new PropertyMetadata(new ObservableCollection<RectOverlay>()));
}

Here's the ViewModel RectOverlay

public class RectOverlay : INotifyPropertyChanged
{
    private double wwidth;
    public double wWidth
    {
        get { return wwidth; }
        set
        {
            wwidth = value;
            OnPropertyChanged(nameof(wWidth));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

I left the XAML the way you had it (except for removing Source=)

If you are sure you need a custom Shape then I can get that going also but also give me your use case for it so that I can get it right please.

Also, I removed the _timer because I wasn't using it to make the example. Don't forget to add it back if you copy and paste.

Michael Puckett II
  • 6,586
  • 5
  • 26
  • 46