Search code examples
c#wpfxamlbindingscaletransform

ScaleTransform binds but does not update when the bound property is changed


I bound ViewScale to Grid's ScaleTransform and when the app starts it correctly scales by 2. But when I change ViewScale by pressing F12, it doesn't trigger ScaleTransform update even though the property value is changed.

Here is the code:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace test
{
    public partial class MainWindow : Window
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void SetField<T> ( ref T field, T value, string propertyName )
        {
            if ( !EqualityComparer<T>.Default.Equals ( field, value ) )
            {
                field = value;
                PropertyChanged?.Invoke ( this, new PropertyChangedEventArgs ( propertyName ) );
            }
        }


        decimal viewScale = 2;
        public decimal ViewScale
        {
            get => this.viewScale;
            set => SetField ( ref this.viewScale, value,
                "ViewScale"
                );
        }

        ObservableCollection<Coin> _coins;
        public ObservableCollection<Coin> Coins { get => _coins; set => SetField ( ref _coins, value, nameof ( _coins ) ); }
        public ICollectionView CollectionView;

        public MainWindow ( )
        {
            this.Coins = new ObservableCollection<Coin> ( );
            for ( int i = 0 ; i < 100 ; ++i )
                this.Coins.Add ( new Coin ( "Coin 1", i ) );

            this.DataContext = this;

            InitializeComponent ( );

            this.PreviewKeyDown += MainWindow_PreviewKeyDown;
        }

        void MainWindow_PreviewKeyDown ( object sender, KeyEventArgs e )
        {
            Console.WriteLine ( e.Key );
            if ( e.Key == Key.Home )
            {
                this.dataGrid.ScrollIntoView ( this.dataGrid.Items [ this.dataGrid.Items.Count - 1 ] );
                this.dataGrid.UpdateLayout ( );
                this.dataGrid.ScrollIntoView ( this.dataGrid.Items [ 0 ] );
            }
            else if ( e.Key == Key.F12 )
            {
                this.ViewScale += 0.1m;
            }
        }

        void MainWindow_KeyDown ( object sender, KeyEventArgs e )
        {

        }
    }

    public class Coin
    {
        public string Symbol { get; set; }
        public int PNL { get; set; }
        public SolidColorBrush Color2 { get; set; }

        public Coin ( string symbol, int pnl )
        {
            this.Symbol = symbol;
            this.PNL = pnl;

            Random rnd = new Random ( );
            Color c = Color.FromRgb ( ( byte ) rnd.Next ( 256 ), ( byte ) rnd.Next ( 256 ), ( byte ) rnd.Next ( 256 ) );

            this.Color2 = new SolidColorBrush ( c );
        }
    }
}

XAML:

<Window x:Class="test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:test"
    mc:Ignorable="d"
    Name="myMainWindow"
    SizeToContent="Width"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Title="Profit Tracker"
    WindowStyle="None"
    Topmost="True"
    Height="426">

    <Window.Resources>

        <Style x:Key="DataGridColumnSeparatorStyle" TargetType="DataGridCell">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Fill="#1e90ff"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="DataGridColumnAlarmStyle" TargetType="DataGridCell">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Fill="#000000"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>



        <Style TargetType="{x:Type DataGrid}">
            <Setter Property="Background" Value="#FFF" />
            <Setter Property="AlternationCount" Value="2" />
            <Setter Property="BorderBrush" Value="Red" />
            <Setter Property="BorderThickness" Value="0" />
        </Style>

        <Style x:Key="RowStyleWithAlternation" TargetType="DataGridRow">
            <Setter Property="Background" Value="#141414"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="FontWeight" Value="Normal"/>
            <Style.Triggers>
                <Trigger Property="AlternationIndex" Value="0">
                    <Setter Property="Background" Value="#141414"/>
                </Trigger>
                <Trigger Property="AlternationIndex" Value="1">
                    <Setter Property="Background" Value="#282828"/>
                </Trigger>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="BorderBrush" Value="Red" />
                    <Setter Property="BorderThickness" Value="1" />
                    <Setter Property="Margin" Value="-1,0,0,0" />
                </Trigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <!--<Setter Property="BorderBrush" Value="#2eff00" />
                    <Setter Property="BorderThickness" Value="1" />-->
                    <Setter Property="Background" Value="Orange"/>
                    <!--<Setter Property="Margin" Value="-1,0,0,0" />-->
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style TargetType="DataGridCell">
            <Setter Property="TextBlock.TextAlignment" Value="Center" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridCell}">
                        <Grid Background="{TemplateBinding Background}">
                            <ContentPresenter VerticalAlignment="Stretch"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="Margin" Value="0"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}" />

            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="BorderBrush" Value="#1e90ff" />
                    <Setter Property="BorderThickness" Value="1" />
                    <!--<Setter Property="Background" Value="Red"/>-->
                </Trigger>



                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Column.DisplayIndex, RelativeSource={RelativeSource Self}}" Value="4"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Property="VerticalAlignment" Value="Stretch"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>

                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Background" Value="{x:Null}"/>
                    <Setter Property="BorderBrush" Value="{x:Null}"/>
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style TargetType="DataGridColumnHeader">
            <Setter Property="HorizontalContentAlignment" Value="Center" />
        </Style>

        <Style TargetType="{x:Type ProgressBar}">
            <Setter Property="Padding" Value="0"/>
            <Setter Property="Margin" Value="0"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ProgressBar">
                        <Border BorderThickness="1" Background="#006400" CornerRadius="0" Padding="0">
                            <Grid x:Name="PART_Track">
                                <Rectangle x:Name="PART_Indicator" HorizontalAlignment="Left" Fill="#75001D" />
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <CollectionViewSource Source="{Binding Coins}" IsLiveSortingRequested="True" x:Key="MyKey" />

    </Window.Resources>

    <Grid>

        <Grid.LayoutTransform>
            <ScaleTransform ScaleX="{Binding ViewScale, ElementName=myMainWindow}" ScaleY="{Binding ViewScale, ElementName=myMainWindow}" />
        </Grid.LayoutTransform>

        <DataGrid Name="dataGrid" ItemsSource="{Binding Source={StaticResource MyKey}}" SelectionMode="Single" GridLinesVisibility="None" HorizontalScrollBarVisibility="Hidden" RowHeaderWidth="0" IsReadOnly="True" CanUserAddRows="False" CanUserResizeColumns="False" CanUserResizeRows="False" AutoGenerateColumns="False" RowStyle="{StaticResource RowStyleWithAlternation}">
            <DataGrid.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
            </DataGrid.Resources>

            <DataGrid.Columns>

                <DataGridTemplateColumn MinWidth="0" Width="2" CellStyle="{StaticResource DataGridColumnSeparatorStyle}"/>
                <DataGridTextColumn Header="PNL" Width="60" SortMemberPath="Balance.UnitPrice" Binding="{Binding Path=PNL}" />

                <DataGridTemplateColumn MinWidth="0" Width="2" CellStyle="{StaticResource DataGridColumnSeparatorStyle}" CanUserSort="False"/>

                <DataGridTemplateColumn Header="Price" Width="60" SortMemberPath="Price">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid>
                                    <Border BorderBrush="#241C59" BorderThickness="1" CornerRadius="1" Background="#2D255B">
                                        <Border BorderBrush="#206fb6" BorderThickness="4" CornerRadius="2" ClipToBounds="True" Margin="-1" HorizontalAlignment="Stretch">
                                            <Border.Effect>
                                                <BlurEffect Radius="10"/>
                                            </Border.Effect>
                                        </Border>
                                    </Border>
                                    <Border Margin="1" VerticalAlignment="Stretch" BorderThickness="0." Background="#69ABDB" HorizontalAlignment="Left" Width="30">
                                        <Border BorderBrush="#38e2ff" BorderThickness="2" CornerRadius="2" ClipToBounds="False">
                                            <Border.Effect>
                                                <BlurEffect Radius="5"/>
                                            </Border.Effect>
                                        </Border>
                                    </Border>
                                </Grid>
                                <TextBlock Text="25%" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                            </Grid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Vol BTC/h" Width="30" SortMemberPath="LastHourVolumeInBtc">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="ABCDE" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                            </Grid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Vol BTC/h" Width="40">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <ProgressBar Value="0.3" Minimum="0" Maximum="1"/>
                                <TextBlock Text="12345" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                            </Grid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTextColumn Header="Net BTC/m" Width="60"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

Solution

  • In order to provide property changed notifications, you have to implement the INotifyPropertyChanged interface in the type that the bound property is defined. You missed this part in your MainWindow code-behind, which is also the data context of your window.

    public partial class MainWindow : Window, INotifyPropertyChanged
    

    Although you defined the PropertyChanged event and your SetField method raises the event correctly, WPF will not be aware of it, if you do not declare the interface on your class definition.