C# XAML/C如何创建具有一个源和多个目标的双向绑定?
我想用三个文本框定义一个时间跨度;一个小时,分,秒。数据验证超出了我的问题范围 我在xaml中定义了三个文本框:C# XAML/C如何创建具有一个源和多个目标的双向绑定?,c#,wpf,xaml,data-binding,uwp,C#,Wpf,Xaml,Data Binding,Uwp,我想用三个文本框定义一个时间跨度;一个小时,分,秒。数据验证超出了我的问题范围 我在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="
<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>
如何在三个文本框和Duration属性之间设置双向绑定
将转换器定义为网格中的静态资源,然后将文本框与转换器和转换器参数绑定到持续时间
convert方法从viewModel属性获取数据,并根据需要进行转换,然后将数据返回到UI文本框
convertback方法从文本框中获取数据并进行转换,然后将其发送到viewmodel属性
我已经向您展示了如何实现这一目标。您只需要弄清楚如何处理convert-back方法,我在这里编写的代码从textbox获取一个字符串,并根据它是否来自哪个textbox ConverterParameter帮助我们,相应地将than-strong转换为TimeSpan对象,然后使用return语句将其指定给viewmodel Duration属性。现在由您决定在将它们发送到Duration之前如何组合它们
暗示
根据您的场景,您需要以某种方式组合小时、分钟和秒,然后将其分配给viewmodel的持续时间。所以我建议一种可能的方法
创建一个可以保存3个静态属性的公共静态类,它们将在Convert和ConvertBack方法中不断更新,这将有助于您进行组合
public static class DurationValues
{
public static string Hours="";
public static string Minutes="";
public static string Seconds="";
}
并具有如下所示的convert类
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的另一个示例:
这是我最终得到的解决方案,99%的灵感来自于Tuseefbsb的答案 以下是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>
请注意,我需要为每个计时器使用不同的转换器实例,因为我依赖于小时、分钟和秒属性。wpf和uwp是两个独立的平台,请仅为应用程序所在的平台添加相应的标记。@tuseefbsb xaml和绑定组件在uwp和wpf之间是通用的。由于我的问题主要涉及绑定,我相信它适用于这两个平台,并且很乐意接受使用其中一个平台的答案。但是,注意到您的观点,我将更改标题以删除UWP。您是否尝试将每个文本框绑定到嵌套属性,如“Duration.Hours”到“Hours”文本框,等等?TimeSpan上的小时、分钟和秒属性是只读的。我想我需要先做一些处理来合并它们。可能只是需要在代码隐藏中完成的情况之一。这真的有效吗?您可以组合这3个属性并创建一个设置了小时、分钟和秒的TimeSpan,还是每次都创建一个新的TimeSpan?它每次都在ConvertBack方法中创建一个新的TimeSpan,如何组合这3个属性实际上取决于您的场景,您必须弄清楚这一点。这是一个好的开始。它确实解决了从Duration属性更新文本框的问题。但正如您所提到的,它并不能解决如何将文本框属性组合成持续时间的问题。我不知道你为什么认为这件事取决于情节。你能详细说明一下吗?我预见到的问题是,我们在转换器中一次只能访问一个文本框值,因此,无法将这三种属性结合起来。@SimCard我更新了我的答案,并添加了一个建议,说明了一种实现所需目标的方法。我之所以说这取决于场景,是因为您在问题中表示不希望在viewmodel中使用三种不同的属性,因为这实际上改变了我们实现它的方式。
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;
}
}
}
<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);
}
}