question

mrw-4494 avatar image
0 Votes"
mrw-4494 asked mrw-4494 commented

Accessing string in ViewModel from IValueConverter

I am trying to create a converter that will take string property from ViewModel, compare it to time passed (time is inputted to TextBox HoursLimitProp in a format 07:30) and then change Label color accordingly. I know this is bad idea to do this way with

var SVM = new SettingsViewModel();


So what is the right way of accessing property in ViewModel from IValueConverter? I have tried to change SVM.HoursLimitProp to text as it is actually a value, but then I am getting null exception.

TimeExceededConverter.cs:

 using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Windows.Data;
 using System.Windows.Media;
    
 namespace Activitytracker
 {
     class TimeExceededConverter : IValueConverter
     {
         #region IValueConverter Members
    
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             string text = (string)value;
             var dateNow = DateTime.Now;
    
             var SVM = new SettingsViewModel();
    
             List<int> TimeSplit = SVM.HoursLimitProp.Split(':').Where(x => int.TryParse(x, out _)).Select(int.Parse).ToList();
    
             DateTime RingTime = new DateTime(dateNow.Year, dateNow.Month, dateNow.Day, TimeSplit[0], TimeSplit[1], 00);
    
             TimeSpan res;
             var result = TimeSpan.TryParseExact(text, @"hh\:mm\:ss", CultureInfo.InvariantCulture, out res);
    
             if (res < RingTime.TimeOfDay)
             {
                 return Brushes.Green;
             }
    
             return Brushes.Red;
         }
    
         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return Binding.DoNothing;
         }
    
         #endregion
     }
 }


SettingsViewModel.cs:

 ....
    
     private string _hoursLimit;
        
     public string HoursLimitProp
     {
         get { return _hoursLimit; }
         set
         {
             if (_hoursLimit != value)
             {
                 _hoursLimit = value;
                 OnPropertyChanged();
             }
         }
     }
    
 ....

MainWindow.xaml:

 ...
    
                 <Label DataContext="{Binding ViewModel}" Style="{StaticResource TopBarLabel_Style}" x:Name="lblTime" 
                    Content="{Binding Path=CurrentTime}" HorizontalAlignment="Left" Height="33" 
                    Foreground="{Binding Path=CurrentTime, Converter={StaticResource TimeExceededConverter}}"
                    Width="89" Grid.Column="1"/>
 ...
windows-wpfdotnet-wpf-xaml
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

DaisyTian-1203 avatar image
1 Vote"
DaisyTian-1203 answered mrw-4494 commented

The TextBox can't invoke PropertyChanged because of the focus on it. I implement it with pressing the Return key after finishing the inputing. Below is my updated parts for your project:

Part 1: Add a MyCommand.cs in your ViewModel File.

 class MyCommand : ICommand
     {
         private Func<object, bool> _canExecute;
         private Action<object> _execute;
    
         public event EventHandler CanExecuteChanged
         {
             add
             {
                 if (_canExecute != null)
                 {
                     CommandManager.RequerySuggested += value;
                 }
             }
             remove
             {
                 if (_canExecute != null)
                 {
                     CommandManager.RequerySuggested -= value;
                 }
             }
         }
    
    
         public bool CanExecute(object parameter)
         {
             if (_canExecute == null) return true;
             return _canExecute(parameter);
         }
    
         public void Execute(object parameter)
         {
             if (_execute != null && CanExecute(parameter))
             {
                 _execute(parameter);
             }
         }
    
         public MyCommand(Action<object> execute) : this(execute, null)
         {
         }
         public MyCommand(Action<object> execute, Func<object, bool> canExecute)
         {
             _execute = execute;
             _canExecute = canExecute;
         }
     }

Part 2: Replace your HoursLimitBox TextBox with below code:

  <TextBox x:Name="HoursLimitBox" Text="{Binding HoursLimitProp,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="36" Margin="133,10,0,0"  VerticalAlignment="Top" Width="166" FontSize="20">
             <TextBox.InputBindings>
                 <KeyBinding Command="{Binding EnterCommand}" Key="Return"></KeyBinding>
             </TextBox.InputBindings>
         </TextBox>

Part 3: Add below code in ViewModel.cs

  private MyCommand _enterCommand;
         public MyCommand EnterCommand
         {
             get
             {
                 if (_enterCommand == null)
                     _enterCommand = new MyCommand(new Action<object>
                         (
                         o =>
                         {
                             HoursLimitConfProp = HoursLimitProp;
                         }
                         ));
                 return _enterCommand;
             }
         }

If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Aha, now I get it. So the key here is to create a custom command to accept the change by implementing custom trigger. Thank you! Now I am a little bit wiser! I will update the repo a little bit later after implementing solution provided by you.

0 Votes 0 ·

And yes, hopefully Microsoft will change the appearance of their code block. I find their new format hardly readable after Visual Studio

0 Votes 0 ·
DaisyTian-1203 avatar image
1 Vote"
DaisyTian-1203 answered mrw-4494 commented

I do some changes for your code to show the label.
Part 1 : Add a constructor in SettingsViewModel.cs to give value to

   public SettingsViewModel()
         {
             _currentTime = System.DateTime.Now.ToString("hh:mm:ss");
             _hoursLimit = System.DateTime.Now.AddMinutes(10).ToString("hh: mm:ss");
         }

Part 2: The updated code for MainWindow.xaml

  <Window.Resources>
         <local:TimeExceededConverter x:Key="TimeExceededConverter"></local:TimeExceededConverter>
     </Window.Resources>
     <Grid>
         <Label x:Name="lblTime"
                Content="{Binding Path=CurrentTime}" 
                Foreground="{Binding Path=CurrentTime, Converter={StaticResource TimeExceededConverter}}"
                HorizontalAlignment="Left" Height="33"  Width="200">
             <Label.DataContext>
                 <local:SettingsViewModel x:Name="ViewModel"></local:SettingsViewModel>
             </Label.DataContext>
         </Label>
     </Grid>

This is my answer based on my understanding, if I misunderstand, please point out.


If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thank you for your answer! I would like to be able to set limit time on the fly from TextBox. I have added example to GitHub to understand my problem better.
https://github.com/vadimffe/DispatherTimer
I mean if I change value in TextBox, I would like to apply this also to IValueConverter

0 Votes 0 ·

@mrw-4494 Did you want to implement the function without clicking the Update button? If you did, I will give you a sample. Otherwise, please give me a more detailed description of your question.

0 Votes 0 ·

Exactly, I would like to implement the function without clicking the Update button.

0 Votes 0 ·