我想用三个文本框定义一个时间跨度;一小时,几分钟和几秒钟。数据验证超出了我的问题范围。
我在xaml中定义了三个文本框:
<UserControl
x:Class="Test_Timer.Timer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Test_Timer"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="timer">
<StackPanel Orientation="Horizontal>
<TextBox x:Name="hoursBox" />
<TextBlock Text=":" />
<TextBox x:Name="minutesBox" />
<TextBlock Text=":" />
<TextBox x:Name="secondsBox" />
</StackPanel>
</UserControl>
我的ViewModel包含一个TimeSpan属性,并在时间更改时发出通知(通过INotifyPropertyChanged)。 ViewModel是通用的,可用于其他视图。为小时,分钟和秒添加三个单独的属性是不可接受的,这样我就可以单独绑定它们。
class TimerVM : ViewModelBase
{
private TimeSpan m_duration = new TimeSpan();
public TimeSpan Duration
{
get { return m_duration; }
set
{
m_duration = value;
NotifyPropertyChanged(nameof(Duration));
}
}
}
如何在三个文本框和Duration属性之间设置双向绑定?
将转换器定义为Grid中的StaticResource,然后将文本框与转换器和转换器参数绑定到持续时间。
<UserControl
x:Class="Test_Timer.Timer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Test_Timer"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="timer">
<Grid>
<Grid.Resources>
<local:DateFormatter x:Key="DurationConverter" />
</Grid.Resources>
<StackPanel Orientation="Horizontal>
<TextBox x:Name="hoursBox" Text="{Binding vm.Duration, Mode=TwoWay,
Converter={StaticResource DurationConverter},
ConverterParameter=hoursBox}"/>
<TextBlock Text=":" />
<TextBox x:Name="minutesBox" Text="{Binding vm.Duration, Mode=TwoWay,
Converter={StaticResource DurationConverter},
ConverterParameter=minutesBox}"/>
<TextBlock Text=":" />
<TextBox x:Name="secondsBox" Text="{Binding vm.Duration, Mode=TwoWay,
Converter={StaticResource DurationConverter},
ConverterParameter=secondsBox}"/>
</StackPanel>
</Grid
</UserControl>
那么你需要在命名空间Test_Timer中的后端定义这个转换器,如下所示:
public class DurationFormatter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, string language)
{
string formatString = parameter as string;
if (formatString == "hoursBox")
{
return ((TimeSpan)value).Hours.ToString();
}
else if (formatString == "minutesBox")
{
return ((TimeSpan)value).Minutes.ToString();
}
else
{
return ((TimeSpan)value).Seconds.ToString();
}
}
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
string formatString = parameter as string;
if (formatString == "hoursBox")
{
return TimeSpan.FromHours(ConvertToInt32(((string)value)));//Here you get the hours value sent from textbox to backend.
}
else if (formatString == "minutesBox")
{
return TimeSpan.FromMinutes(ConvertToInt32(((string)value)));//Here you get the minutes value sent from textbox to backend.
}
else
{
return TimeSpan.FromSeconds(ConvertToInt32(((string)value)));//Here you get the seconds value sent from textbox to backend.
}
}
}
convert方法从viewModel属性获取数据并根据需要进行转换,然后将数据返回到UI(文本框)。
convertback方法从您的文本框中获取数据并转换它然后将其发送到您的viewmodel属性。
我已经告诉你如何实现这一目标。你只需要弄清楚你想要如何处理转换方法,我在这里写的代码从文本框中获取一个字符串,然后相应地将其转换为一个TimeSpan对象(取决于它是来自哪个文本框ConverterParameter帮助我们在那里),然后使用return语句将它分配给viewmodel Duration属性。现在,您可以在将它们发送到持续时间之前将它们组合起来。
建议
根据您的情况,您需要以某种方式组合小时,分钟和秒,然后将其分配给视图模型的持续时间。所以我建议一种可能的方法来做到这一点。
创建一个可以容纳3个静态属性的公共静态类,它们将在Convert和ConvertBack方法中不断更新,这样可以帮助你进行组合。
public static class DurationValues
{
public static string Hours="";
public static string Minutes="";
public static string Seconds="";
}
并将转换类如下所示。
public class DurationFormatter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, string language)
{
string formatString = parameter as string;
if (formatString == "hoursBox")
{
string rValue = ((TimeSpan)value).Hours.ToString();
DurationValues.Hours=rValue;
return rValue;
}
else if (formatString == "minutesBox")
{
string rValue = ((TimeSpan)value).Minutes.ToString();
DurationValues.Minutes=rValue;
return rValue;
}
else
{
string rValue = ((TimeSpan)value).Seconds.ToString();
DurationValues.Seconds=rValue;
return rValue;
}
}
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
string formatString = parameter as string;
if (formatString == "hoursBox")
{
DurationValues.Hours = (string)value;
var ts = new TimeSpan (DurationValues.Hours,DurationValues.Minutes,DurationValues.Seconds);
return ts;
}
else if (formatString == "minutesBox")
{
DurationValues.Minutes = (string)value;
var ts = new TimeSpan (DurationValues.Hours,DurationValues.Minutes,DurationValues.Seconds);
return ts;
}
else
{
DurationValues.Seconds = (string)value;
var ts = new TimeSpan (DurationValues.Hours,DurationValues.Minutes,DurationValues.Seconds);
return ts;
}
}
}
IValueConverter的另一个例子:https://www.wpf-tutorial.com/data-binding/value-conversion-with-ivalueconverter/
这是我最终得到的解决方案(99%受到touseefbsb答案的启发)。
这是XAML:
<UserControl
x:Class="Test_Timer.Timer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Test_Timer"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="timer">
<UserControl.Resources>
<local:TimeSpanConverter x:Key="TimeSpanConverter" />
<UserControl.Resources>
<Grid>
<StackPanel Orientation="Horizontal>
<TextBox x:Name="hoursBox" Text="{Binding vm.Duration, Mode=TwoWay,
Converter={StaticResource TimeSpanConverter},
ConverterParameter=hours}"/>
<TextBlock Text=":" />
<TextBox x:Name="minutesBox" Text="{Binding vm.Duration, Mode=TwoWay,
Converter={StaticResource TimeSpanConverter},
ConverterParameter=minutes}"/>
<TextBlock Text=":" />
<TextBox x:Name="secondsBox" Text="{Binding vm.Duration, Mode=TwoWay,
Converter={StaticResource TimeSpanConverter},
ConverterParameter=seconds}"/>
</StackPanel>
</Grid>
</UserControl>
和转换器:
class TimeSpanConverter : IValueConverter
{
public int Hours { get; set; }
public int Minutes { get; set; }
public int Seconds { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{
string strParam = (string)parameter;
TimeSpan ts = (TimeSpan)value;
switch(strParam.ToLower())
{
case "hours":
return ts.Hours.ToString();
case "minutes":
return ts.Minutes.ToString();
case "seconds":
return ts.Seconds.ToString();
}
return "0";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
string strParam = (string)parameter;
int intVal = int.Parse((string)value);
switch (strParam.ToLower())
{
case "hours":
Hours = intVal;
break;
case "minutes":
Minutes = intVal;
break;
case "seconds":
Seconds = intVal;
break;
}
return new TimeSpan(Hours, Minutes, Seconds);
}
}
请注意,我需要为每个计时器使用不同的转换器实例,因为我依赖于Hour,Minute和Second Properties。