如何在WPF中将两个元素绑定到单个源

时间:2015-08-17 16:01:33

标签: wpf data-binding

我的情况是有两个控件。您可以在其中设置分钟,然后在其中指定秒数。

它们都应绑定到视图模型中的单个属性。此属性的类型为字符串。该字符串的格式为[hh:mm:ss]。因此,在“分钟”控件中更改值应更改字符串的“mm”部分,并在“秒”控件中更改值应更改字符串的'ss'部分。

提前致谢

2 个答案:

答案 0 :(得分:1)

如果您使用TimeSpan并且其范围介于0到59h 59s之间,则这是一个3属性的ViewModel工作解决方案。我没有完全测试,条件/验证将根据要求进行更改。我使用了TimeSpan.TotalSeconds,因为这是我们需要的解决方案;这意味着,当将TimeSpan设置为新值时,我们只需设置通过公共属性的总秒数。另一种方法是在ViewModel中拥有2个TimeSpan属性,然后在设置公共属性时,可以调用_item.TotalSeconds = VMMinutes.TotalSeconds + VMSeconds.TotalSeconds.TotalSeconds。基本上你在这里有很多设计选择。

<强> MainWindow.xaml:

<Grid>
    <StackPanel>
        <Border Height="60" BorderBrush="Black" BorderThickness="1">
            <StackPanel Orientation="Horizontal">
                <Label Content="Minutes"/>
                <TextBox Text="{Binding Minutes}" />
                <Label Content="Seconds"/>
                <TextBox Text="{Binding Seconds}" />
            </StackPanel>
        </Border>
        <Border Height="60" BorderBrush="Black" BorderThickness="1">
            <StackPanel Orientation="Horizontal">
                <Label Content="Total Seconds"/>
                <TextBox Text="{Binding TotalSeconds}" />
            </StackPanel>
        </Border>
    </StackPanel>
</Grid>

<强> MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ItemViewModel(new Item(new TimeSpan(0, 3, 59)));
    }
}

<强> ItemViewModel.cs

public class ItemViewModel : INotifyPropertyChanged
{
    private readonly Item _item;

    public event PropertyChangedEventHandler PropertyChanged;

    public ItemViewModel(Item item)
    {
        _item = item;
    }

    public string TotalSeconds
    {
        get
        {
            return _item.TotalSeconds.ToString();
        }
        set
        {
            double newTotSecs;
            if(!string.IsNullOrEmpty(value))
            {
                if(double.TryParse(value, out newTotSecs))
                {
                    _item.TotalSeconds = newTotSecs;
                    NotifyPropertyChanged();
                    NotifyPropertyChanged("Minutes");
                    NotifyPropertyChanged("Seconds");
                }
            }
        }
    }
    public string Seconds
    {
        get
        {
            return (_item.TotalSeconds % 60).ToString();
        }
        set
        {
            int newVal;
            if(!string.IsNullOrEmpty(value))
            {
                if(int.TryParse(value, out newVal))
                {
                    if(newVal >= 0 && newVal <= 59)
                    {
                        int totMinSec;
                        if(int.TryParse(Minutes, out totMinSec))
                        {
                            _item.TotalSeconds = (totMinSec * 60) + newVal;
                            NotifyPropertyChanged();
                            NotifyPropertyChanged("TotalSeconds");
                        }
                    }
                }
            }
        }
    }

    public string Minutes
    {
        get
        {
            return ((int)(_item.TotalSeconds / 60)).ToString();
        }
        set
        {
            int newVal;
            if(!string.IsNullOrEmpty(value))
            {
                if(int.TryParse(value, out newVal))
                {
                    if(newVal >= 0 && newVal <= 59)
                    {
                        int totSec;
                        if(int.TryParse(Seconds, out totSec))
                        {
                            _item.TotalSeconds = totSec + (newVal * 60);
                            NotifyPropertyChanged();
                            NotifyPropertyChanged("TotalSeconds");
                        }
                    }
                }
            }
        }
    }

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

<强> Item.cs

public class Item
{
    private TimeSpan _time;
    public double TotalSeconds
    {
        get
        {
            return _time.TotalSeconds;
        }
        set
        {
            if(value >= 0)
            {
                _time = new TimeSpan(0, 0, (int)value);
            }
        }
    }
    public Item(TimeSpan time)
    {
        _time = time;
    }
}

enter image description here

注意:您的另一个选择是使用Converter,我还没有提供解决方案。我认为从长远来看它可能会变得更干净,因为你真正需要来回传递的是转换器的总秒数。

答案 1 :(得分:1)

我会使用上面的NETScape方法,但将其封装在用户控件中。用户控件XAML类似于:

<UserControl>
    <Grid x:Name="LayoutRoot">
       <Grid.RowDefinitions>
           <RowDefinition Height="*"/>
           <RowDefinition Height="*"/>
       </Grid.RowDefinitions>
       <Grid.ColumnDefinitions>
           <ColumnDefinition Width="*"/>
           <ColumnDefinition Width="*"/>
       </Grid.ColumnDefinitions>
       <TextBlock Text="Minutes" Grid.Row="0" Grid.Column="0"/>
       <TextBox Text="{Binding InternalMinutes}" Grid.Row="0" Grid.Column="1"/>

       <TextBlock Text="Seconds" Grid.Row="1" Grid.Column="0"/>
       <TextBox Text="{Binding InternalSeconds}" Grid.Row="1" Grid.Column="1"/>
    </Grid>
</UserControl>

然后在代码隐藏中,您将拥有实际DateTime对象的依赖属性,以及要绑定的属性(您可以使用视图模型,或者只是从{{1}开始当它的所有View逻辑时,它没问题!)。

示例属性是:

TextChanged

同样,这里有多种方法,您可以使用转换器来使用中间对象。 public int InternalSeconds { get { return ExternalTime.Seconds; } set { ExternalTime.Seconds = value; NotifyPropertyChanged(); } } 是此处的DP,如果您希望在此控件之外更改值,请务必处理其ExternalTime事件。