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