asusralis
Topic Author
Posts: 86
Joined: 30 Jul 2018, 05:03

Changing a property in a custom control changes all elements of that type.

23 Jul 2020, 19:43

Like the title says, when I change a property of a control it inconsistently changes all controls of that type as well.

I attached a project that recreates this. In Assets/Views/Root.xaml, if you set the last NineSlice control's ImageSource property to "{StaticResource AltBorder}" it will change all of the NineSlice controls' ImageSource. You will also notice that the first NineSlice control is set to AltBorder, but it does not show as such.

This also shows the problem I submitted here about RelativeSource TemplatedParent not working correctly - I am forced to set that property in the code behind.

Project:

Tags:
 
User avatar
sfernandez
Site Admin
Posts: 1935
Joined: 22 Dec 2011, 19:20

Re: Changing a property in a custom control changes all elements of that type.

24 Jul 2020, 11:07

As described in this post: viewtopic.php?f=3&t=2052
you are sharing the same resource for all controls, so the last control that sets the ImageSource property of the resource is changing it for all instances of the control.

You should define the resources inside the visual tree so they can be different for each instance:
    <Style TargetType="{x:Type local:NineSlice}">
        <Setter Property="ImageSource" Value="{StaticResource DefaultBorder}" />
        <Setter Property="SliceThickness" Value="4" />
        <Setter Property="BorderThickness" Value="20" />
        <Setter Property="Padding" Value="0" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:NineSlice}">
                    <Grid x:Name="Root">
                        <Grid.Resources>
                            <Style TargetType="Rectangle">
                                <Setter Property="SnapsToDevicePixels" Value="True" />
                                <Setter Property="RenderOptions.BitmapScalingMode" Value="NearestNeighbor" />
                            </Style>
                        </Grid.Resources>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="{Binding BorderThickness.Left, RelativeSource={RelativeSource TemplatedParent}}" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="{Binding BorderThickness.Right, RelativeSource={RelativeSource TemplatedParent}}" />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="{Binding BorderThickness.Top, RelativeSource={RelativeSource TemplatedParent}}" />
                            <RowDefinition Height="*" />
                            <RowDefinition Height="{Binding BorderThickness.Bottom, RelativeSource={RelativeSource TemplatedParent}}" />
                        </Grid.RowDefinitions>
                        <Rectangle x:Name="TopLeft" Grid.Row="0" Grid.Column="0">
                          <Rectangle.Fill>
                            <ImageBrush
                                ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImageSource}"
                                ViewboxUnits="Absolute" />
                          </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle x:Name="TopCenter" Grid.Row="0" Grid.Column="1">
                          <Rectangle.Fill>
                            <ImageBrush
                                ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImageSource}"
                                ViewboxUnits="Absolute" />
                          </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle x:Name="TopRight" Grid.Row="0" Grid.Column="2">
                          <Rectangle.Fill>
                            <ImageBrush
                                ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImageSource}"
                                ViewboxUnits="Absolute" />
                          </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle x:Name="CenterLeft" Grid.Row="1" Grid.Column="0">
                          <Rectangle.Fill>
                            <ImageBrush
                                ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImageSource}"
                                ViewboxUnits="Absolute" />
                          </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle x:Name="CenterRight" Grid.Row="1" Grid.Column="2">
                          <Rectangle.Fill>
                            <ImageBrush
                                ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImageSource}"
                                ViewboxUnits="Absolute" />
                          </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle x:Name="BottomLeft" Grid.Row="2" Grid.Column="0">
                          <Rectangle.Fill>
                            <ImageBrush
                                ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImageSource}"
                                ViewboxUnits="Absolute" />
                          </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle x:Name="BottomCenter" Grid.Row="2" Grid.Column="1">
                          <Rectangle.Fill>
                            <ImageBrush
                                ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImageSource}"
                                ViewboxUnits="Absolute" />
                          </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle x:Name="BottomRight" Grid.Row="2" Grid.Column="2">
                          <Rectangle.Fill>
                            <ImageBrush
                                ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImageSource}"
                                ViewboxUnits="Absolute" />
                          </Rectangle.Fill>
                        </Rectangle>
                        <Grid x:Name="Middle" Grid.Row="1" Grid.Column="1">
                            <Rectangle x:Name="Center">
                              <Rectangle.Fill>
                                <ImageBrush
                                    ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ImageSource}"
                                    ViewboxUnits="Absolute" />
                              </Rectangle.Fill>
                            </Rectangle>
                            <ContentPresenter x:Name="MiddleContent" Margin="{TemplateBinding Padding}" />
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
        public NineSlice()
        {
            Loaded += (s, e) =>
            {
                ApplyTemplate();

                _root = (Grid)Template.FindName("Root", this);

                if (_root != null)
                {
                    _tLeft = (ImageBrush)((Rectangle)Template.FindName("TopLeft", this)).Fill;
                    _t = (ImageBrush)((Rectangle)Template.FindName("TopCenter", this)).Fill;
                    _tRight = (ImageBrush)((Rectangle)Template.FindName("TopRight", this)).Fill;
                    _cLeft = (ImageBrush)((Rectangle)Template.FindName("CenterLeft", this)).Fill;
                    _c = (ImageBrush)((Rectangle)Template.FindName("Center", this)).Fill;
                    _cRight = (ImageBrush)((Rectangle)Template.FindName("CenterRight", this)).Fill;
                    _bLeft = (ImageBrush)((Rectangle)Template.FindName("BottomLeft", this)).Fill;
                    _b = (ImageBrush)((Rectangle)Template.FindName("BottomCenter", this)).Fill;
                    _bRight = (ImageBrush)((Rectangle)Template.FindName("BottomRight", this)).Fill;

                    UpdateRects();
                }
            };
        }
 
asusralis
Topic Author
Posts: 86
Joined: 30 Jul 2018, 05:03

Re: Changing a property in a custom control changes all elements of that type.

24 Jul 2020, 12:26

But doesn't this work in WPF? And the same with the other problem? I have multiple NineSlice controls with different borders and none are changing when I change an individual ImageSource. Please correct me if I'm wrong, but there doesn't seem to be any problem with this. Sorry for being so WPF centric in these posts, but I usually test my problem in WPF before posting here, so it's odd that I can't seem to make it break in WPF.
Attachments
WpfApp4.rar
(97.87 KiB) Downloaded 17 times
 
User avatar
sfernandez
Site Admin
Posts: 1935
Joined: 22 Dec 2011, 19:20

Re: Changing a property in a custom control changes all elements of that type.

24 Jul 2020, 17:19

Sorry, I was doing a different test , defining the resource in ControlTemplate.Resources, instead of the resources of an element of the Template visual tree.

I have verified we are deviating from WPF in case resources are part of the Resources property of an element in the Template visual tree.
I created a ticket #1759 to follow this issue. In the meantime the workaround is as I said, define the ImageBrush directly in the Rectangles, instead of using StaticResources, sorry for the inconvenience.
 
asusralis
Topic Author
Posts: 86
Joined: 30 Jul 2018, 05:03

Re: Changing a property in a custom control changes all elements of that type.

24 Jul 2020, 17:24

No problem! Thank you so much for helping me. And just to make sure I understand, this and my other thread both stems from the same problem that is explained in that bug report?
 
User avatar
sfernandez
Site Admin
Posts: 1935
Joined: 22 Dec 2011, 19:20

Re: Changing a property in a custom control changes all elements of that type.

24 Jul 2020, 17:27

Yes, the source of the problem is the same. As the resource is being shared, TemplatedParent binding can't be correctly resolved, and also, changing a property on that resource affects all controls.

Who is online

Users browsing this forum: Bing [Bot] and 4 guests