C# IValueConverter-使用源和转换器在同一绑定/同一控件中的源和转换器

C# IValueConverter-使用源和转换器在同一绑定/同一控件中的源和转换器,c#,wpf,C#,Wpf,我有一组组合框和文本框,如下所示: class ViewModel : INotifyPropertyChanged { // The actual time. Similar to the "timeAll" field you have in the code now // Should be kept in UTC private DateTime _time; // The three selected TimeZoneInfo values for t

我有一组组合框和文本框,如下所示:

class ViewModel : INotifyPropertyChanged
{
    // The actual time. Similar to the "timeAll" field you have in the code now
    // Should be kept in UTC
    private DateTime _time;

    // The three selected TimeZoneInfo values for the combo boxes
    private TimeZoneInfo _timeZone1;
    private TimeZoneInfo _timeZone2;
    private TimeZoneInfo _timeZone3;

    public DateTime Time
    {
        get { return _time; }
        set { UpdateValue(ref _time, value); }
    }

    public TimeZoneInfo TimeZone1
    {
        get { return _timeZone1; }
        set { UpdateValue(ref _timeZone1, value); }
    }

    public TimeZoneInfo TimeZone2
    {
        get { return _timeZone2; }
        set { UpdateValue(ref _timeZone2, value); }
    }

    public TimeZoneInfo TimeZone3
    {
        get { return _timeZone3; }
        set { UpdateValue(ref _timeZone3, value); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void UpdateValue<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (!object.Equals(field, value))
        {
            field = value;
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
class TimeConverter : IMultiValueConverter
{
    public string Format { get; set; }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        DateTime utc = (DateTime)values[0];
        TimeZoneInfo tzi = (TimeZoneInfo)values[1];

        return tzi != null ? TimeZoneInfo.ConvertTime(utc, tzi).ToString(Format) : Binding.DoNothing;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        string timeText = (string)value;
        DateTime time;

        if (!DateTime.TryParseExact(timeText, Format, null, DateTimeStyles.None, out time))
        {
            return new object[] { Binding.DoNothing, Binding.DoNothing };
        }

        ComboBox comboBox = (ComboBox)parameter;
        TimeZoneInfo tzi = (TimeZoneInfo)comboBox.SelectedValue;

        return new object[] { TimeZoneInfo.ConvertTime(time, tzi, TimeZoneInfo.Utc), Binding.DoNothing };
    }
}
C1T1

C2T2

C3T3

我实现了一个IValueConverter来在C1中设置时区,并在T1中获得相应的时间。其他几对也一样

我想做的是:如果用户手动更改T1中的时间,则T2和T3中的时间必须根据T1以及时区进行更改

T1不是参考。如果任何文本框的值已更改,则所有其他文本框也必须更改

这种变化可能发生在:

  • 如果在组合框中更改了时区

  • 如果用户通过在文本框中键入来手动更改时间

  • 这是我的完整代码:

     public partial class MainWindow : Window 
    {
        public static int num;
        public static bool isUserInteraction;
        public static DateTime timeAll;
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }
    
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ReadOnlyCollection<TimeZoneInfo> TimeZones = TimeZoneInfo.GetSystemTimeZones();
    
            this.DataContext = TimeZones;
    
            cmb_TZ1.SelectedIndex = 98;
            cmb_TZ2.SelectedIndex = 46;
            cmb_TZ3.SelectedIndex = 84;
            cmb_TZ4.SelectedIndex = 105;
            cmb_TZ5.SelectedIndex = 12;
        }
    
        private void ComboBox_Selection(object Sender, SelectionChangedEventArgs e)
             {
            var cmbBox = Sender as ComboBox;
    
            DateTime currTime = DateTime.UtcNow;
            TimeZoneInfo tst = (TimeZoneInfo)cmbBox.SelectedItem;
            if (isUserInteraction)
            {
               /*  txt_Ctry1.Text=
                  txt_Ctry2.Text =
                  txt_Ctry3.Text =
                  txt_Ctry4.Text =
                  txt_Ctry5.Text =*/
                isUserInteraction = false;
            }
        }
    
      private void TextBox_Type(object Sender, TextChangedEventArgs e)
       {
         var txtBox = Sender as TextBox;
    
            if (isUserInteraction)
            {
                timeAll = DateTime.Parse(txtBox.Text); 
                if (txtBox.Name != "txt_Ctry1")
                 txt_Ctry1.Text=
                           if (txtBox.Name != "txt_Ctry2")
                    txt_Ctry2.Text =
                           if (txtBox.Name != "txt_Ctry3")
                    txt_Ctry3.Text =
                           if (txtBox.Name != "txt_Ctry4")
                    txt_Ctry4.Text =
                           if (txtBox.Name != "txt_Ctry5")
                    txt_Ctry5.Text =
                isUserInteraction = false;
            }
        }
    
    
        private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            isUserInteraction = true;
        }
    }
    public class TimeZoneConverter : IValueConverter
    {
        public object Convert(
            object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (MainWindow.isUserInteraction == false)
            {
                return value == null ? string.Empty : TimeZoneInfo
                    .ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, (TimeZoneInfo)value)
                    .ToString("HH:mm:ss dd MMM yy");
            }
     else
            {
                return value == null ? string.Empty : TimeZoneInfo
               .ConvertTime(MainWindow.timeAll, TimeZoneInfo.Utc, (TimeZoneInfo)value)
               .ToString("HH:mm:ss dd MMM yy");
            }
    
                 }
    
        public object ConvertBack(
            object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    
       <Window x:Class="Basic_WorldClock.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:src="clr-namespace:System;assembly=mscorlib"
        xmlns:sys="clr-namespace:System;assembly=System.Core"
        xmlns:local="clr-namespace:Basic_WorldClock"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
    
      <Window.Resources>
      <ObjectDataProvider x:Key="timezone" ObjectType="{x:Type     
       sys:TimeZoneInfo}" MethodName="GetSystemTimeZones">
      </ObjectDataProvider>
          <local:TimeZoneConverter x:Key="timezoneconverter"/>
       </Window.Resources>
    
       <Grid Margin="0,0.909,0,-0.909">
    
        <TextBox x:Name="txt_Time1" Text="{Binding ElementName=cmb_TZ1, Path=SelectedValue, Converter={StaticResource timezoneconverter}}" VerticalAlignment="Top"/>
        <TextBox x:Name="txt_Time2"  Text="{Binding ElementName=cmb_TZ2, Path=SelectedValue, Converter={StaticResource timezoneconverter}}" VerticalAlignment="Top"/>
        <TextBox x:Name="txt_Time3" Text="{Binding ElementName=cmb_TZ3, Path=SelectedValue, Converter={StaticResource timezoneconverter}}" Height="23.637" VerticalAlignment="Bottom"/>
    
        <ComboBox x:Name="cmb_TZ1" SelectionChanged="ComboBox_Selection" PreviewMouseDown="OnPreviewMouseDown" ItemsSource="{Binding Source={StaticResource timezone}}" HorizontalAlignment="Right" Height="22.667" Margin="0,89.091,51.667,0" VerticalAlignment="Top" Width="144.666"/>
        <ComboBox x:Name="cmb_TZ2" SelectionChanged="ComboBox_Selection" PreviewMouseDown="OnPreviewMouseDown" ItemsSource="{Binding Source={StaticResource timezone}}" HorizontalAlignment="Right" Height="22.667" Margin="0,131.091,52.667,0" VerticalAlignment="Top" Width="144.666"/>
        <ComboBox x:Name="cmb_TZ3" SelectionChanged="ComboBox_Selection" PreviewMouseDown="OnPreviewMouseDown" ItemsSource="{Binding Source={StaticResource timezone}}" HorizontalAlignment="Right" Height="22.667" Margin="0,0,48.334,123.575" VerticalAlignment="Bottom" Width="144.666"/>
    
    
    </Grid>
    
    公共部分类主窗口:窗口
    {
    公共静态整数;
    公共静态bool-isUserInteraction;
    公共静态日期时间所有;
    公共主窗口()
    {
    初始化组件();
    this.DataContext=this;
    }
    已加载私有无效窗口(对象发送器、路由目标)
    {
    ReadOnlyCollection TimeZones=TimeZoneInfo.GetSystemTimeZones();
    this.DataContext=时区;
    cmb_TZ1.SelectedIndex=98;
    cmb_TZ2.SelectedIndex=46;
    cmb_TZ3.SelectedIndex=84;
    cmb_TZ4.SelectedIndex=105;
    cmb_TZ5.SelectedIndex=12;
    }
    私有无效组合框\u选择(对象发送方,SelectionChangedEventArgs e)
    {
    var cmbBox=发送方作为组合框;
    DateTime currTime=DateTime.UtcNow;
    TimeZoneInfo tst=(TimeZoneInfo)cmbBox.SelectedItem;
    if(isUserInteraction)
    {
    /*txt_Ctry1.Text=
    txt_Ctry2.Text=
    txt_Ctry3.Text=
    txt_Ctry4.Text=
    txt_Ctry5.Text=*/
    isUserInteraction=false;
    }
    }
    私有无效文本框_类型(对象发送方,TextChangedEventArgs e)
    {
    var txtBox=发送方作为文本框;
    if(isUserInteraction)
    {
    timeAll=DateTime.Parse(txtBox.Text);
    如果(txtBox.Name!=“txt_Ctry1”)
    txt_Ctry1.Text=
    如果(txtBox.Name!=“txt_Ctry2”)
    txt_Ctry2.Text=
    如果(txtBox.Name!=“txt_Ctry3”)
    txt_Ctry3.Text=
    如果(txtBox.Name!=“txt_Ctry4”)
    txt_Ctry4.Text=
    如果(txtBox.Name!=“txt_Ctry5”)
    txt_Ctry5.Text=
    isUserInteraction=false;
    }
    }
    私有void OnPreviewMouseDown(对象发送器,鼠标按钮ventargs e)
    {
    isUserInteraction=true;
    }
    }
    公共类时区转换器:IValueConverter
    {
    公共对象转换(
    对象值、类型targetType、对象参数、CultureInfo区域性)
    {
    if(MainWindow.isUserInteraction==false)
    {
    返回值==null?字符串。空:TimeZoneInfo
    .ConvertTime(DateTime.UtcNow,TimeZoneInfo.Utc,(TimeZoneInfo)值)
    .ToString(“HH:mm:ss dd MMM yy”);
    }
    其他的
    {
    返回值==null?字符串。空:TimeZoneInfo
    .ConvertTime(MainWindow.timeAll、TimeZoneInfo.Utc、(TimeZoneInfo)值)
    .ToString(“HH:mm:ss dd MMM yy”);
    }
    }
    公共对象转换回(
    对象值、类型targetType、对象参数、CultureInfo区域性)
    {
    抛出新的NotSupportedException();
    }
    }
    
    }

    XAML:

     public partial class MainWindow : Window 
    {
        public static int num;
        public static bool isUserInteraction;
        public static DateTime timeAll;
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }
    
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ReadOnlyCollection<TimeZoneInfo> TimeZones = TimeZoneInfo.GetSystemTimeZones();
    
            this.DataContext = TimeZones;
    
            cmb_TZ1.SelectedIndex = 98;
            cmb_TZ2.SelectedIndex = 46;
            cmb_TZ3.SelectedIndex = 84;
            cmb_TZ4.SelectedIndex = 105;
            cmb_TZ5.SelectedIndex = 12;
        }
    
        private void ComboBox_Selection(object Sender, SelectionChangedEventArgs e)
             {
            var cmbBox = Sender as ComboBox;
    
            DateTime currTime = DateTime.UtcNow;
            TimeZoneInfo tst = (TimeZoneInfo)cmbBox.SelectedItem;
            if (isUserInteraction)
            {
               /*  txt_Ctry1.Text=
                  txt_Ctry2.Text =
                  txt_Ctry3.Text =
                  txt_Ctry4.Text =
                  txt_Ctry5.Text =*/
                isUserInteraction = false;
            }
        }
    
      private void TextBox_Type(object Sender, TextChangedEventArgs e)
       {
         var txtBox = Sender as TextBox;
    
            if (isUserInteraction)
            {
                timeAll = DateTime.Parse(txtBox.Text); 
                if (txtBox.Name != "txt_Ctry1")
                 txt_Ctry1.Text=
                           if (txtBox.Name != "txt_Ctry2")
                    txt_Ctry2.Text =
                           if (txtBox.Name != "txt_Ctry3")
                    txt_Ctry3.Text =
                           if (txtBox.Name != "txt_Ctry4")
                    txt_Ctry4.Text =
                           if (txtBox.Name != "txt_Ctry5")
                    txt_Ctry5.Text =
                isUserInteraction = false;
            }
        }
    
    
        private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            isUserInteraction = true;
        }
    }
    public class TimeZoneConverter : IValueConverter
    {
        public object Convert(
            object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (MainWindow.isUserInteraction == false)
            {
                return value == null ? string.Empty : TimeZoneInfo
                    .ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, (TimeZoneInfo)value)
                    .ToString("HH:mm:ss dd MMM yy");
            }
     else
            {
                return value == null ? string.Empty : TimeZoneInfo
               .ConvertTime(MainWindow.timeAll, TimeZoneInfo.Utc, (TimeZoneInfo)value)
               .ToString("HH:mm:ss dd MMM yy");
            }
    
                 }
    
        public object ConvertBack(
            object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    
       <Window x:Class="Basic_WorldClock.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:src="clr-namespace:System;assembly=mscorlib"
        xmlns:sys="clr-namespace:System;assembly=System.Core"
        xmlns:local="clr-namespace:Basic_WorldClock"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
    
      <Window.Resources>
      <ObjectDataProvider x:Key="timezone" ObjectType="{x:Type     
       sys:TimeZoneInfo}" MethodName="GetSystemTimeZones">
      </ObjectDataProvider>
          <local:TimeZoneConverter x:Key="timezoneconverter"/>
       </Window.Resources>
    
       <Grid Margin="0,0.909,0,-0.909">
    
        <TextBox x:Name="txt_Time1" Text="{Binding ElementName=cmb_TZ1, Path=SelectedValue, Converter={StaticResource timezoneconverter}}" VerticalAlignment="Top"/>
        <TextBox x:Name="txt_Time2"  Text="{Binding ElementName=cmb_TZ2, Path=SelectedValue, Converter={StaticResource timezoneconverter}}" VerticalAlignment="Top"/>
        <TextBox x:Name="txt_Time3" Text="{Binding ElementName=cmb_TZ3, Path=SelectedValue, Converter={StaticResource timezoneconverter}}" Height="23.637" VerticalAlignment="Bottom"/>
    
        <ComboBox x:Name="cmb_TZ1" SelectionChanged="ComboBox_Selection" PreviewMouseDown="OnPreviewMouseDown" ItemsSource="{Binding Source={StaticResource timezone}}" HorizontalAlignment="Right" Height="22.667" Margin="0,89.091,51.667,0" VerticalAlignment="Top" Width="144.666"/>
        <ComboBox x:Name="cmb_TZ2" SelectionChanged="ComboBox_Selection" PreviewMouseDown="OnPreviewMouseDown" ItemsSource="{Binding Source={StaticResource timezone}}" HorizontalAlignment="Right" Height="22.667" Margin="0,131.091,52.667,0" VerticalAlignment="Top" Width="144.666"/>
        <ComboBox x:Name="cmb_TZ3" SelectionChanged="ComboBox_Selection" PreviewMouseDown="OnPreviewMouseDown" ItemsSource="{Binding Source={StaticResource timezone}}" HorizontalAlignment="Right" Height="22.667" Margin="0,0,48.334,123.575" VerticalAlignment="Bottom" Width="144.666"/>
    
    
    </Grid>
    
    
    

    问题:如何像combox一样使用Convert方法将相应的更改级联到其他文本框? 我可以使用TextChanged方法捕获引用文本框中的更改,并且可以添加几行代码来进行这些更改,但是我想使用IValueConverter。我可以将源代码和转换器放在文本框的同一个绑定中吗?

    如果没有一个好的文件来清楚地显示您到目前为止所获得的内容,就很难提供准确的信息。但是根据您所描述的,这里的主要问题似乎是您没有使用WPF设计用于的基于“视图模型”的技术。此外,您正在将
    Text
    属性绑定到时区,而不是时间,因此当
    Text
    属性更改时,WPF想要更新的实际上是组合框选择,而不是时间本身

    您需要做的第一件事是,不要让控件相互引用,而是创建一个表示要显示的实际状态的视图模型类,然后将其用于窗口中的
    DataContext
    ,将适当的属性绑定到特定控件

    这些属性是什么?根据你的描述,你实际上只有四个:1)实际时间,2)4)你想要处理的三个时区

    比如说:

    class ViewModel : INotifyPropertyChanged
    {
        // The actual time. Similar to the "timeAll" field you have in the code now
        // Should be kept in UTC
        private DateTime _time;
    
        // The three selected TimeZoneInfo values for the combo boxes
        private TimeZoneInfo _timeZone1;
        private TimeZoneInfo _timeZone2;
        private TimeZoneInfo _timeZone3;
    
        public DateTime Time
        {
            get { return _time; }
            set { UpdateValue(ref _time, value); }
        }
    
        public TimeZoneInfo TimeZone1
        {
            get { return _timeZone1; }
            set { UpdateValue(ref _timeZone1, value); }
        }
    
        public TimeZoneInfo TimeZone2
        {
            get { return _timeZone2; }
            set { UpdateValue(ref _timeZone2, value); }
        }
    
        public TimeZoneInfo TimeZone3
        {
            get { return _timeZone3; }
            set { UpdateValue(ref _timeZone3, value); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void UpdateValue<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (!object.Equals(field, value))
            {
                field = value;
                PropertyChangedEventHandler handler = PropertyChanged;
    
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
    
    class TimeConverter : IMultiValueConverter
    {
        public string Format { get; set; }
    
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            DateTime utc = (DateTime)values[0];
            TimeZoneInfo tzi = (TimeZoneInfo)values[1];
    
            return tzi != null ? TimeZoneInfo.ConvertTime(utc, tzi).ToString(Format) : Binding.DoNothing;
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            string timeText = (string)value;
            DateTime time;
    
            if (!DateTime.TryParseExact(timeText, Format, null, DateTimeStyles.None, out time))
            {
                return new object[] { Binding.DoNothing, Binding.DoNothing };
            }
    
            ComboBox comboBox = (ComboBox)parameter;
            TimeZoneInfo tzi = (TimeZoneInfo)comboBox.SelectedValue;
    
            return new object[] { TimeZoneInfo.ConvertTime(time, tzi, TimeZoneInfo.Utc), Binding.DoNothing };
        }
    }
    
    XAML看起来像这样:

    class ViewModel : INotifyPropertyChanged
    {
        // The actual time. Similar to the "timeAll" field you have in the code now
        // Should be kept in UTC
        private DateTime _time;
    
        // The three selected TimeZoneInfo values for the combo boxes
        private TimeZoneInfo _timeZone1;
        private TimeZoneInfo _timeZone2;
        private TimeZoneInfo _timeZone3;
    
        public DateTime Time
        {
            get { return _time; }
            set { UpdateValue(ref _time, value); }
        }
    
        public TimeZoneInfo TimeZone1
        {
            get { return _timeZone1; }
            set { UpdateValue(ref _timeZone1, value); }
        }
    
        public TimeZoneInfo TimeZone2
        {
            get { return _timeZone2; }
            set { UpdateValue(ref _timeZone2, value); }
        }
    
        public TimeZoneInfo TimeZone3
        {
            get { return _timeZone3; }
            set { UpdateValue(ref _timeZone3, value); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void UpdateValue<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (!object.Equals(field, value))
            {
                field = value;
                PropertyChangedEventHandler handler = PropertyChanged;
    
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
    
    class TimeConverter : IMultiValueConverter
    {
        public string Format { get; set; }
    
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            DateTime utc = (DateTime)values[0];
            TimeZoneInfo tzi = (TimeZoneInfo)values[1];
    
            return tzi != null ? TimeZoneInfo.ConvertTime(utc, tzi).ToString(Format) : Binding.DoNothing;
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            string timeText = (string)value;
            DateTime time;
    
            if (!DateTime.TryParseExact(timeText, Format, null, DateTimeStyles.None, out time))
            {
                return new object[] { Binding.DoNothing, Binding.DoNothing };
            }
    
            ComboBox comboBox = (ComboBox)parameter;
            TimeZoneInfo tzi = (TimeZoneInfo)comboBox.SelectedValue;
    
            return new object[] { TimeZoneInfo.ConvertTime(time, tzi, TimeZoneInfo.Utc), Binding.DoNothing };
        }
    }
    
    
    
    这将在运行时正常工作。但是,由于在
    ConverterParameter
    赋值中使用了
    {x:reference…}
    ,您将收到设计时uquot对象引用未设置为对象实例”错误消息。对一些人来说,这是一个小小的不便,但我觉得这是一个巨大的烦恼,我愿意去f