XAML / C#如何使用一个源和多个目标创建双向绑定?

问题描述 投票:-1回答:2

我想用三个文本框定义一个时间跨度;一小时,几分钟和几秒钟。数据验证超出了我的问题范围。

我在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属性之间设置双向绑定?

c# wpf xaml data-binding uwp
2个回答
2
投票

将转换器定义为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/


1
投票

这是我最终得到的解决方案(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。

© www.soinside.com 2019 - 2024. All rights reserved.