0

I'm trying to bind a property to a textblock without success. I've got a "Notifier" class which contains the INotifyPropertyChanged event. Model classes inherit from the "Notifier" class.

namespace MyOffice.Models
{public class Notifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string name)
    {
        System.Diagnostics.Debug.WriteLine("OnProperty called");
        if (PropertyChanged!=null)
        {
            System.Diagnostics.Debug.WriteLine("PropertyChanged called");
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}

}

The "Online" class follows:

namespace MyOffice.Models
{
    public class Online : Notifier
    {
        private string _dbUser;        
        internal string DbUser { get; set; }

        internal System.Security.SecureString DbPass { get; set; }

        private string _progressMessage;

        public string ProgressMessage
        {
            get => _progressMessage;
            set
            {
                _progressMessage = value;
                System.Diagnostics.Debug.WriteLine("progress message changed");
                OnPropertyChanged("ProgressMessage");
            }
        }


    }
}

Class "All" serves as the nest of various model classes like Online and others.

namespace MyOffice.Models
{
    public class AllModels : Notifier
    {
        public Doc Doc {get; set;}
        public Online Online {get; set;}
        public AllModels()
        {
            Doc = new Doc();
            Online = new Online();
        }
    }
}

And here is the xaml:

<Window x:Class="MyOffice.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"
        mc:Ignorable="d"
        Title="blahblah" Height="768" Width="1366" FontSize="16"  FontFamily="Arial" WindowStartupLocation="CenterScreen" WindowState="Maximized" Cursor="Arrow" Icon="Resources/medical-bag.ico" Background="DarkCyan" Closing="MainWin_Closing" WindowStyle="None" DataContext="All">

        <Window.Resources>
        <Style x:Key="leftPanelButtons" TargetType="{x:Type Button}">
            <Setter Property="FontFamily" Value="Georgia"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="DockPanel.Dock" Value="Top"/>
            <Setter Property="Height" Value="Auto"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Margin" Value="0,0,0,0"/>
            <Setter Property="Background" Value="ForestGreen"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border Background="{TemplateBinding Background}" BorderBrush="ForestGreen" BorderThickness="0">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>

            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="DarkSeaGreen"/>
                </Trigger>
            </Style.Triggers>

        </Style>

        <Style x:Key="upperPanelButtons" TargetType="{x:Type Button}">
            <Setter Property="Background" Value="DarkCyan"/>
            <Setter Property="FontFamily" Value="Georgia"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="DockPanel.Dock" Value="Top"/>
            <Setter Property="Height" Value="Auto"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Margin" Value="0,0,0,0"/>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border Background="{TemplateBinding Background}" BorderBrush="DarkCyan" BorderThickness="0">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>

            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="DarkTurquoise"/>
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style x:Key="exitButtons" TargetType="{x:Type Button}">
            <Setter Property="Background" Value="DarkRed"/>
            <Setter Property="FontFamily" Value="Georgia"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="DockPanel.Dock" Value="Top"/>
            <Setter Property="Height" Value="Auto"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Margin" Value="0,0,0,0"/>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border Background="{TemplateBinding Background}" BorderBrush="DarkRed" BorderThickness="0">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>

            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="IndianRed"/>
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style x:Key="invisibleTabs" TargetType="{x:Type TabItem}">
            <Setter Property="Visibility" Value="Hidden"/>
            <Setter Property="Height" Value="0"/>
            <Setter Property="Margin" Value="0,0,0,0"/>
        </Style>

    </Window.Resources>

    <Grid x:Name="gridMain" Margin="0,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <Grid.RowDefinitions>
            <RowDefinition Height="46" />
            <!--<RowDefinition Height="35"/>-->
            <RowDefinition Height="*"/>
            <RowDefinition Height="35"/>
        </Grid.RowDefinitions>
        <!--<TextBlock Grid.Row="0" x:Name="txbName" HorizontalAlignment="Left" Margin="5,5,0,0" TextWrapping="Wrap" Text="Όνοματεπώνυμο" VerticalAlignment="Top" Height="18" Width="130"/>-->

        <UniformGrid Grid.Row="0" Width="Auto" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Rows="1">
            <UniformGrid.Resources>
                <Style BasedOn="{StaticResource upperPanelButtons}" TargetType="Button"/>
            </UniformGrid.Resources>

            <Button Content="1" Click="Button_Click_1"/>
            <Button Content="2" Click="Button_Click_2"/>
            <Button Content="3" Click="Button_Click_3"/>
            <Button Content="4" Click="Button_Click_4"/>
            <Button Content="5" Click="Button_5"/>
            <Button Content="6" Style="{StaticResource exitButtons}" Click="Button_Click_UpperPanelExit"/>

        </UniformGrid>

        <DockPanel Grid.Row="1">
            <DockPanel Height="Auto" Width="Auto" VerticalAlignment="Stretch">
                <UniformGrid Columns="1" Height="Auto" VerticalAlignment="Stretch" MinWidth="150" Width="Auto">
                    <UniformGrid.Resources>
                        <Style BasedOn="{StaticResource leftPanelButtons}" TargetType="Button"/>
                    </UniformGrid.Resources>

                    <Button Click="Click_Button_asdfasdf">
                        aadfasdf
                    </Button>

                    <Button Click="Click_Button_efefefe">
                        efefefe
                    </Button>

                    <Button Click="Click_Button_t5t5t5t">
                        t5t5t5t5t5
                    </Button>

                    <Button Click="Click_Button_jujujujuj">
                        jujujujuju
                    </Button>

                    <Button Click="Click_Button_lklklklkl">
                        klklklklkl
                    </Button>

                    <Button Click="Click_Button_dkdkdkd">
                        dkdkdkdk
                    </Button>

                    <Button Click="Click_Button_fawf">
                        wefwef
                    </Button>

                </UniformGrid>

                <DockPanel Height="Auto" Width="Auto" Background="PaleGoldenRod" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0,0,0,0">
                    <Grid>
                        <Frame x:Name="mainFrame"></Frame>
                    </Grid>
                </DockPanel>

            </DockPanel>
        </DockPanel>

        <Border Grid.Row="2" Margin="5,0,5,0" Height="24" Width="1366" >
            <TextBlock  x:Name="txbProgress" TextWrapping="NoWrap" Width="1366" Background="DarkCyan" Foreground="White" Text="{Binding Path=All.Online.ProgressMessage, Mode=OneWay}" TextAlignment="Center"/>
        </Border>

    </Grid>

</Window>

And - finally - this is the MainWindow code:

namespace MyOffice
{
    public partial class MainWindow : Window
    {
        public Models.AllModels All {get; set;}

        public MainWindow()
        {           
            InitializeComponent();
            All = new Models.AllModels();            
            DataContext = All;
            All.Online.ProgressMessage = "progressing...";
        }
     }
}

Whenever the property (progressMessage) is changed the "OnPropertyChanged" method is run but "PropertyChanged" is always null... and the execution never reaches the following statement:

PropertyChanged(this, new PropertyChangedEventArgs(name));

therefore the textblock never shows any "progressMessage". Why is it always null? Is there something wrong with the program? Please note that I've changed internal to public (both the class and the related property) and it still won't work...

pzogr
  • 424
  • 1
  • 12
  • 30
  • WPF data binding only works with public properties. – Clemens Jan 31 '19 at 10:41
  • @Clemens Thanks for the suggestion! It still won't work though, even after changing the class and property to public. – pzogr Jan 31 '19 at 11:28
  • `public Online Online;` is not a property. – Clemens Jan 31 '19 at 11:30
  • @Clemens The property is "ProgressMessage", which belongs to the class Online. – pzogr Jan 31 '19 at 11:34
  • `Binding Path=All.Online.ProgressMessage` - all elements in a Binding Path must be public properties. Please take a closer look at [Data Binding Overview](https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/data-binding-overview) – Clemens Jan 31 '19 at 11:36
  • @Clemens I don't get it. Could you please explain or suggest a modification? I believe I've done the same as this guy here: http://www.blackwasp.co.uk/WPFBindingOptions.aspx. Or have I not? I've got a class and this class contains properties (and other stuff) and I've set the datacontext to the class. Is this wrong? I've also tried instantiating only the class "Online" but it makes no difference. PropertyChanged is always null. – pzogr Jan 31 '19 at 11:52
  • I already said that `public Online Online;` is not a property, but of course should be one. Don't you know the difference between a property and a field? It could be `public Online Online { get; }` – Clemens Jan 31 '19 at 11:56
  • @Clemens I've changed them to properties - both the Online class and the All class. No progress still... – pzogr Jan 31 '19 at 12:17
  • If you have `Binding Path=All.Online.ProgressMessage` it is required that there is an `All` property in the current DataContext, which you don't have. Either set `DataContext = this` or change the Binding to `Path=Online.ProgressMessage`. As a general hint, you may always inspect the Output Window in Visual Studio for binding error messages. – Clemens Jan 31 '19 at 12:20
  • @Clemens There is a binding error indeed. VS cannot find ProgressMessage. I did change everything to public properties though and the result is the same... Besides that in the link I've mentioned this guy uses a class not a property [DataContext = new classname()] – pzogr Jan 31 '19 at 12:30
  • As already said, in order to make `Path=All.Online.ProgressMessage` work, you have to set `DataContext = this;` – Clemens Jan 31 '19 at 12:38
  • @Clemens Thanks for your effort. It works with 'this'. I don't understand why (haha) but it does. I thought that passing 'All' in datacontext means that all of its properties would be available for binding. – pzogr Jan 31 '19 at 12:45
  • That would mean that the `All` object in the DataContext had an `All` property. Does it? No. – Clemens Jan 31 '19 at 12:46
  • @Clemens Still don't understand but thanks anyway. I suppose I have to do some serious reading on binding... – pzogr Jan 31 '19 at 12:54
  • Maybe it helps to rename the All property in MainWindow to ViewModel. Then assign `DataContext = ViewModel;` and bind to its properties by e.g. `Path=Online.ProgressMessage`. Now `ViewModel` does no longer need to be a property. You may even just use a local variable `var vm = new Models.AllModels();` and assign `DataContext = vm;` – Clemens Jan 31 '19 at 12:58
  • @Clemens It does work this way. How is this different to naming the class 'All'? And why would 'All' have to be a property and 'ViewModel' wouldn't? – pzogr Jan 31 '19 at 13:04
  • It's not about "naming the class". The important point is that there is some object in the DataContext. The Binding Path is relative to that object. It is completely irrelevant if you store the object reference somewhere else, in a property or a field or a local variable. In other words, WPF doesn't care if there is an `All` property in your MainWindow when you assign the object held by that property to the DataContext. – Clemens Jan 31 '19 at 13:10
  • @Clemens I see now... I should have removed 'All' from the path since 'All' was implied by setting the DataContext to 'All'. That was a very enlightening conversation. Thank you very much! – pzogr Jan 31 '19 at 13:52

0 Answers0