XAML / C # Как создать двустороннюю привязку с одним источником и несколькими целями? - PullRequest
0 голосов
/ 16 сентября 2018

Я хочу определить промежуток времени, используя три текстовых поля; один для часов, минут и секунд. Проверка данных выходит за рамки моего вопроса.

Я определяю три текстовых поля в 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?

Ответы [ 2 ]

0 голосов
/ 17 сентября 2018

Вот решение, которое я выбрал (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.

0 голосов
/ 17 сентября 2018

Определите конвертер как 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 и преобразует их по своему желанию, а затем возвращает данные в ваш пользовательский интерфейс (текстовое поле).

Метод convertback получает данные из вашего текстового поля и преобразует их, а затем отправляет их в свойство viewmodel.

Я показал вам, как вы можете этого достичь. Вам нужно только выяснить, как вы хотите работать с методом convert back, код, который я здесь написал, получает строку из текстового поля и соответствующим образом преобразует ее в объект TimeSpan (в зависимости от того, является ли он исходя из того, какое текстовое поле ConverterParameter помогает нам в этом), а затем назначает его свойству viewmodel Duration с помощью оператора return. Теперь вам решать, как вы хотите объединить их перед отправкой в ​​Duration.

Предложение

В зависимости от вашего сценария вам нужно как-то объединить часы, минуты и секунды, а затем назначить их для Duration вашей модели просмотра. Поэтому я предлагаю 1 возможный способ сделать это.

создает открытый статический класс, который может содержать 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 / привязка данных / преобразование значения-с-ivalueconverter /

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...