Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. WPF
  4. DependencyProperty fires only in ViewModel.ctor

DependencyProperty fires only in ViewModel.ctor

Scheduled Pinned Locked Moved WPF
wpfdebugginghelpquestion
5 Posts 2 Posters 26 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • I Offline
    I Offline
    iwangoll
    wrote on last edited by
    #1

    Hello, I'd tried to reduce the problem. I've this UserControl

    public class TestControl : Label
    {
    public static readonly DependencyProperty dependencyPropertyTestInfo =
    DependencyProperty.Register("TestInfo", typeof(string), typeof(TestControl),
    new PropertyMetadata("", new PropertyChangedCallback(ChangeTestInfo)));

    public string TestInfo
    {
        get => (string)GetValue(dependencyPropertyTestInfo);
        set => SetValue(dependencyPropertyTestInfo, value);
    }
    private static void ChangeTestInfo(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var testControl = (TestControl)d;
        testControl.TestInfo = (string)e.NewValue;
    }
    

    }

    This is the essential part of MainWindow.xaml:

    Its bounded in MainWindow.xaml.cs to DataContext with this ViewModel:

    public class MainWindowViewModel : BindableBase
    {
    private int counter;
    private string _testInfoView;
    public string TestInfoView
    {
    get { return _testInfoView; }
    set { SetProperty(ref _testInfoView, value); }
    }
    public DelegateCommand NewTestInfoCommand { get; }
    public MainWindowViewModel()
    {
    NewTestInfoCommand = new DelegateCommand(OnNewTestInfoCommand);
    TestInfoView = "Info from ctor";
    }
    private void OnNewTestInfoCommand()
    {
    string newInfo = $"Info No. {++counter}";
    Debug.WriteLine($"{MethodBase.GetCurrentMethod().Name}: {nameof(TestInfoView)}=«{TestInfoView}» -> «{newInfo}»");
    TestInfoView = newInfo;
    }
    }

    I've added a "Debug.WriteLine" in MainWindow.xaml.cs

    private void BtnShow_Click(object sender, RoutedEventArgs e)
    {
    Debug.WriteLine($"{MethodBase.GetCurrentMethod().Name}: {nameof(tct.TestInfo)}={tct.TestInfo}");
    }

    After starting the app 1. I've clicked "BtnShow" this is the debug-info: BtnShow_Click: TestInfo=Info from ctor 2. I've clicked "BtnNewTestInfo" this is the debug-info: OnNewTestInfoCommand: TestInfoView=«Info from ctor» -> «Info No. 1» 3. I've clicked "BtnShow" this is the debug-info: BtnShow_Click: TestInfo=Info from ctor Now my question is: why View

    Richard DeemingR 1 Reply Last reply
    0
    • I iwangoll

      Hello, I'd tried to reduce the problem. I've this UserControl

      public class TestControl : Label
      {
      public static readonly DependencyProperty dependencyPropertyTestInfo =
      DependencyProperty.Register("TestInfo", typeof(string), typeof(TestControl),
      new PropertyMetadata("", new PropertyChangedCallback(ChangeTestInfo)));

      public string TestInfo
      {
          get => (string)GetValue(dependencyPropertyTestInfo);
          set => SetValue(dependencyPropertyTestInfo, value);
      }
      private static void ChangeTestInfo(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
          var testControl = (TestControl)d;
          testControl.TestInfo = (string)e.NewValue;
      }
      

      }

      This is the essential part of MainWindow.xaml:

      Its bounded in MainWindow.xaml.cs to DataContext with this ViewModel:

      public class MainWindowViewModel : BindableBase
      {
      private int counter;
      private string _testInfoView;
      public string TestInfoView
      {
      get { return _testInfoView; }
      set { SetProperty(ref _testInfoView, value); }
      }
      public DelegateCommand NewTestInfoCommand { get; }
      public MainWindowViewModel()
      {
      NewTestInfoCommand = new DelegateCommand(OnNewTestInfoCommand);
      TestInfoView = "Info from ctor";
      }
      private void OnNewTestInfoCommand()
      {
      string newInfo = $"Info No. {++counter}";
      Debug.WriteLine($"{MethodBase.GetCurrentMethod().Name}: {nameof(TestInfoView)}=«{TestInfoView}» -> «{newInfo}»");
      TestInfoView = newInfo;
      }
      }

      I've added a "Debug.WriteLine" in MainWindow.xaml.cs

      private void BtnShow_Click(object sender, RoutedEventArgs e)
      {
      Debug.WriteLine($"{MethodBase.GetCurrentMethod().Name}: {nameof(tct.TestInfo)}={tct.TestInfo}");
      }

      After starting the app 1. I've clicked "BtnShow" this is the debug-info: BtnShow_Click: TestInfo=Info from ctor 2. I've clicked "BtnNewTestInfo" this is the debug-info: OnNewTestInfoCommand: TestInfoView=«Info from ctor» -> «Info No. 1» 3. I've clicked "BtnShow" this is the debug-info: BtnShow_Click: TestInfo=Info from ctor Now my question is: why View

      Richard DeemingR Offline
      Richard DeemingR Offline
      Richard Deeming
      wrote on last edited by
      #2

      XAML data-binding to dependency properties does not use the property getter or setter. It calls SetValue directly on the DependencyProperty instance. Any code that needs to execute when the property value changes needs to be in the PropertyChangedCallback method. #118 – Don’t Add Code to Dependency Property Getter/Setter | 2,000 Things You Should Know About WPF[^] You've also forgotten to raise the PropertyChanged event from your viewmodel. Assuming the usual definition of BindableBase, you probably meant to write something like:

      private string _testInfoView;
      public string TestInfoView
      {
      get { return _testInfoView; }
      set { SetProperty(ref _testInfoView, value); }
      }

      The SetProperty method will update the field and raise the PropertyChanged event for you.


      "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

      "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

      I 1 Reply Last reply
      0
      • Richard DeemingR Richard Deeming

        XAML data-binding to dependency properties does not use the property getter or setter. It calls SetValue directly on the DependencyProperty instance. Any code that needs to execute when the property value changes needs to be in the PropertyChangedCallback method. #118 – Don’t Add Code to Dependency Property Getter/Setter | 2,000 Things You Should Know About WPF[^] You've also forgotten to raise the PropertyChanged event from your viewmodel. Assuming the usual definition of BindableBase, you probably meant to write something like:

        private string _testInfoView;
        public string TestInfoView
        {
        get { return _testInfoView; }
        set { SetProperty(ref _testInfoView, value); }
        }

        The SetProperty method will update the field and raise the PropertyChanged event for you.


        "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

        I Offline
        I Offline
        iwangoll
        wrote on last edited by
        #3

        Thank you for your answer. I have changed my test project and adapted the original message. 1. removed all Debug.Writeline in TestControl 2. in ViewModel

        set { SetProperty(ref _testInfoView, value); }

        3. Added a button BtnShow to see in it's Click in xaml.cs the content of TestInfo property In the original post under "After starting the app" I'd explained what I've done. But the ViewModel-command didn't change the property. I've added the code to GitHub - iwangoll/TestBinding: ViewModel-command didn't change the DependencyProperty.[^]

        Richard DeemingR 1 Reply Last reply
        0
        • I iwangoll

          Thank you for your answer. I have changed my test project and adapted the original message. 1. removed all Debug.Writeline in TestControl 2. in ViewModel

          set { SetProperty(ref _testInfoView, value); }

          3. Added a button BtnShow to see in it's Click in xaml.cs the content of TestInfo property In the original post under "After starting the app" I'd explained what I've done. But the ViewModel-command didn't change the property. I've added the code to GitHub - iwangoll/TestBinding: ViewModel-command didn't change the DependencyProperty.[^]

          Richard DeemingR Offline
          Richard DeemingR Offline
          Richard Deeming
          wrote on last edited by
          #4

          That's a strange way of sharing code - uploading a 7zip file to GitHub, rather than uploading your code itself. :laugh: The first thing that jumps out is the PropertyChangedCallback:

          private static void ChangeTestInfo(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
          var testControl = (TestControl)d;
          testControl.TestInfo = (string)e.NewValue;
          }

          Thankfully, WPF doesn't call the "property changed" callback if you set the property to the same value it's already set; otherwise, you'd end up with a stack overflow here. As far as I can see, you want to log the fact that the property has changed, not change the property again:

          private static void ChangeTestInfo(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
          Debug.WriteLine($"TestInfo changed from '{e.OldValue}' to '{e.NewValue}'.");
          }

          Beyond that, your code works as expected. Clicking the "set new TestInfo" button shows the new value being set in the debug console. Clicking the "show TestInfo by Button-Click in xaml.cs" button shows the expected new value in the debug console. Perhaps you were expecting the content of your test control to update? But that's not going to happen: you've set it to the fixed string "TestControl", which isn't bound to anything. If you want to show the value on the screen, then change your markup to:

          {Binding Path=TestInfo, ElementName=tct}" x:Name="tct" TestInfo="{Binding TestInfoView}"/>


          "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

          "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

          I 1 Reply Last reply
          0
          • Richard DeemingR Richard Deeming

            That's a strange way of sharing code - uploading a 7zip file to GitHub, rather than uploading your code itself. :laugh: The first thing that jumps out is the PropertyChangedCallback:

            private static void ChangeTestInfo(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
            var testControl = (TestControl)d;
            testControl.TestInfo = (string)e.NewValue;
            }

            Thankfully, WPF doesn't call the "property changed" callback if you set the property to the same value it's already set; otherwise, you'd end up with a stack overflow here. As far as I can see, you want to log the fact that the property has changed, not change the property again:

            private static void ChangeTestInfo(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
            Debug.WriteLine($"TestInfo changed from '{e.OldValue}' to '{e.NewValue}'.");
            }

            Beyond that, your code works as expected. Clicking the "set new TestInfo" button shows the new value being set in the debug console. Clicking the "show TestInfo by Button-Click in xaml.cs" button shows the expected new value in the debug console. Perhaps you were expecting the content of your test control to update? But that's not going to happen: you've set it to the fixed string "TestControl", which isn't bound to anything. If you want to show the value on the screen, then change your markup to:

            {Binding Path=TestInfo, ElementName=tct}" x:Name="tct" TestInfo="{Binding TestInfoView}"/>


            "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

            I Offline
            I Offline
            iwangoll
            wrote on last edited by
            #5

            Dear Richard, many thanks for your help. Now it works. And I think I understand the functionality of DependencyProperty much better now. And sorry for the GitHub & 7z. I'm very new there.

            1 Reply Last reply
            0
            Reply
            • Reply as topic
            Log in to reply
            • Oldest to Newest
            • Newest to Oldest
            • Most Votes


            • Login

            • Don't have an account? Register

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • World
            • Users
            • Groups