为什么绑定仅适用于一个

时间:2015-05-29 08:04:02

标签: c# wpf

我有一个用户控件,如下所示: enter image description here

和xaml代码:

<UserControl x:Class="Customizing.Views.IpRangeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:ig="http://schemas.infragistics.com/xaml"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:mvvm="http://www.galasoft.ch/mvvmlight"
             xmlns:range="clr-namespace:Customizing.Views"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300"
             DataContext="{Binding IpRangeVm, Source={StaticResource Locator}}">
    <Grid>
        <ig:ThemeManager.Theme>
            <ig:Office2013Theme StyleMicrosoftControls="True" />
        </ig:ThemeManager.Theme>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <TabControl Grid.Row="0" ItemsSource="{Binding Locations}">

            <TabControl.ItemTemplate>
                <DataTemplate>
                    <Label Content="{Binding Name}" FontSize="16" FontWeight="Bold" />
                </DataTemplate>
            </TabControl.ItemTemplate>

            <TabControl.ContentTemplate>
                <DataTemplate>
                    <ScrollViewer VerticalScrollBarVisibility="Auto">
                        <ItemsControl ItemsSource="{Binding Addresses}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <UniformGrid Columns="2" />
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <Border Margin="20,15,20,15" Padding="15,20,15,20">
                                        <range:IpRangeFields Start="{Binding Start}" End="{Binding End}"
                                                             Subnet="{Binding Subnet}" Gateway="{Binding Gateway}">
                                            <i:Interaction.Triggers>
                                                <i:EventTrigger EventName="Error">
                                                    <mvvm:EventToCommand Command="{Binding Path=DataContext.ErrorCmd, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}}" />
                                                </i:EventTrigger>
                                            </i:Interaction.Triggers>
                                        </range:IpRangeFields>
                                    </Border>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </ScrollViewer>
                </DataTemplate>

            </TabControl.ContentTemplate>
        </TabControl>

    </Grid>
</UserControl>

正如您在图片中看到的那样,只有子网字段填充了值,为什么绑定,例如start,end和gateway不起作用?我做错了什么

<range:IpRangeFields Start="{Binding Start}" End="{Binding End}"
                     Subnet="{Binding Subnet}" Gateway="{Binding Gateway}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Error">
            <mvvm:EventToCommand Command="{Binding Path=DataContext.ErrorCmd, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</range:IpRangeFields>

视图模型和视图绑定到:

using System.Collections.ObjectModel;
using System.Diagnostics;
using Customizing.Models;
using Customizing.Services;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;

namespace Customizing.ViewModel
{
    public class IpRangeViewModel : ViewModelBase
    {
        private readonly IDataService _dataService;

        public IpRangeViewModel(IDataService dataService)
        {
            _dataService = dataService;
            _dataService.QueryIpRanges((ranges, error) => { Locations = ranges; });

            ErrorCmd = new RelayCommand(() => { Debug.WriteLine("Error occurs"); });
        }

        public ObservableCollection<LocationRange> Locations { get; set; }
        public RelayCommand ErrorCmd { get; set; }
    }
}

和模型

using System.Collections.ObjectModel;

namespace Customizing.Models
{
    public class LocationRange
    {
        public string Name { get; set; }
        public ObservableCollection<IpRange> Addresses { get; set; }
    }
}

Usercontrol iprangefield:

<UserControl x:Class="Customizing.Views.IpRangeFields"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:ig="http://schemas.infragistics.com/xaml"
             xmlns:local="clr-namespace:Customizing.Views"
             xmlns:net="clr-namespace:System.Net;assembly=System"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:behaviors="clr-namespace:Customizing.Behaviors"
             xmlns:system="clr-namespace:System;assembly=mscorlib"
             mc:Ignorable="d"
             x:Name="_parent"
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <system:String x:Key="InputMaskIp">000.000.000.000</system:String>
    </UserControl.Resources>
    <Grid DataContext="{Binding ElementName=_parent}">
        <Grid.RowDefinitions>
            <RowDefinition Height="60" />
            <RowDefinition Height="10" />
            <RowDefinition Height="60" />
            <RowDefinition Height="10" />
            <RowDefinition Height="60" />
            <RowDefinition Height="10" />
            <RowDefinition Height="60" />
            <RowDefinition Height="10" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="2.5*" />
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Content="Start" VerticalContentAlignment="Center" FontSize="18"
               HorizontalContentAlignment="Center" FontWeight="Bold" />
        <Label Grid.Row="2" Grid.Column="0" Content="End" VerticalContentAlignment="Center" FontSize="18"
               HorizontalContentAlignment="Center" FontWeight="Bold" />
        <Label Grid.Row="4" Grid.Column="0" Content="Subnet" VerticalContentAlignment="Center" FontSize="18"
               HorizontalContentAlignment="Center" FontWeight="Bold" />
        <Label Grid.Row="6" Grid.Column="0" Content="Gateway" VerticalContentAlignment="Center" FontSize="18"
               HorizontalContentAlignment="Center" FontWeight="Bold" />

        <TextBox VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Row="0" Grid.Column="1"
                 FontSize="22" Validation.Error="_ValidationError">
            <Binding Path="Start" ValidatesOnNotifyDataErrors="True" UpdateSourceTrigger="PropertyChanged"
                     NotifyOnValidationError="true">
                <Binding.ValidationRules>
                    <local:IpAddressRule />
                </Binding.ValidationRules>
            </Binding>
            <i:Interaction.Behaviors>
                <behaviors:TextBoxInputMaskBehavior InputMask="{StaticResource InputMaskIp}" PromptChar="0" />
            </i:Interaction.Behaviors>
        </TextBox>

        <TextBox VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Row="2" Grid.Column="1"
                 FontSize="22" Validation.Error="_ValidationError">
            <Binding Path="End" ValidatesOnNotifyDataErrors="True" UpdateSourceTrigger="PropertyChanged"
                     NotifyOnValidationError="true">
                <Binding.ValidationRules>
                    <local:IpAddressRule />
                </Binding.ValidationRules>
            </Binding>
            <i:Interaction.Behaviors>
                <behaviors:TextBoxInputMaskBehavior InputMask="{StaticResource InputMaskIp}" PromptChar="0" />
            </i:Interaction.Behaviors>
        </TextBox>

        <TextBox VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Row="4" Grid.Column="1"
                 FontSize="22" Validation.Error="_ValidationError">
            <Binding Path="Subnet" ValidatesOnNotifyDataErrors="True" UpdateSourceTrigger="PropertyChanged"
                     NotifyOnValidationError="true">
                <Binding.ValidationRules>
                    <local:IpAddressRule />
                </Binding.ValidationRules>
            </Binding>
            <i:Interaction.Behaviors>
                <behaviors:TextBoxInputMaskBehavior InputMask="{StaticResource InputMaskIp}" PromptChar="0" />
            </i:Interaction.Behaviors>
        </TextBox>

        <TextBox VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Row="6" Grid.Column="1"
                 FontSize="22" Validation.Error="_ValidationError">
            <Binding Path="Gateway" ValidatesOnNotifyDataErrors="True" UpdateSourceTrigger="PropertyChanged"
                     NotifyOnValidationError="true">
                <Binding.ValidationRules>
                    <local:IpAddressRule />
                </Binding.ValidationRules>
            </Binding>
            <i:Interaction.Behaviors>
                <behaviors:TextBoxInputMaskBehavior InputMask="{StaticResource InputMaskIp}" PromptChar="0" />
            </i:Interaction.Behaviors>
        </TextBox>


    </Grid>
</UserControl>

和背后的代码:

using System;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Windows;
using System.Windows.Controls;

namespace Customizing.Views
{
    /// <summary>
    ///     Interaction logic for IpRangeFields.xaml
    /// </summary>
    public partial class IpRangeFields : UserControl
    {
        public static readonly DependencyProperty StartProperty = DependencyProperty.Register("Start", typeof (string),
            typeof (IpRangeFields), new PropertyMetadata(null));

        public static readonly DependencyProperty EndProperty = DependencyProperty.Register("End", typeof (string),
            typeof (IpRangeFields), new PropertyMetadata(null));

        public static readonly DependencyProperty SubnetProperty = DependencyProperty.Register("Subnet", typeof (string),
            typeof (IpRangeFields), new PropertyMetadata(null));

        public static readonly DependencyProperty GatewayProperty = DependencyProperty.Register("Gateway",
            typeof (string), typeof (IpRangeFields), new PropertyMetadata(null));

        // Register the routed event
        public static readonly RoutedEvent ErrorEvent =
            EventManager.RegisterRoutedEvent("Error", RoutingStrategy.Bubble,
            typeof(RoutedEventHandler), typeof(IpRangeFields));

        public IpRangeFields()
        {
            InitializeComponent();
        }

        public event RoutedEventHandler Error
        {
            add { AddHandler(ErrorEvent, value); }
            remove { RemoveHandler(ErrorEvent, value); }
        }

        public string Start
        {
            get { return (string) GetValue(StartProperty); }
            set { SetValue(StartProperty, value); }
        }

        public string End
        {
            get { return (string) GetValue(EndProperty); }
            set { SetValue(EndProperty, value); }
        }

        public string Subnet
        {
            get { return (string) GetValue(SubnetProperty); }
            set { SetValue(SubnetProperty, value); }
        }

        public string Gateway
        {
            get { return (string) GetValue(GatewayProperty); }
            set { SetValue(GatewayProperty, value); }
        }

        private void _ValidationError(object sender, ValidationErrorEventArgs e)
        {
            RaiseEvent(new RoutedEventArgs(ErrorEvent, sender));
        }

    }

    public class IpAddressRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            IPAddress ip;
            if (!IPAddress.TryParse(value.ToString(), out ip))
            {
                return new ValidationResult(false, "IP address is not valid.");
            }

            return new ValidationResult(true, null);
        }
    }
}

2 个答案:

答案 0 :(得分:1)

请先看一下这个答案:Hair loss and MVVM user controls

现在我复制并粘贴wpf usercontrols的一般答案

如果您使用依赖关系属性创建UserControl,那么您的绑定应始终包含某种&#34; 相对绑定&#34; - 我总是使用 elementname binding 。所以你的usercontrol绑定应该是这样的。

<UserControl x:Name="uc">
<StackPanel Orientation="Horizontal">
  <Image Source="{Binding Source={x:Static helper:ImageHelper.JumpLabelImage}}" Width="16" Height="16" VerticalAlignment="Center"/>
  <TextBlock >
    <Hyperlink Command="{Binding ElementName=uc, Path=JumpCommand, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
               CommandParameter="{Binding ElementName=uc, Path=CommandParameter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
        <TextBlock Text="{Binding ElementName=uc, Path=LabelText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" VerticalAlignment="Center" />
    </Hyperlink>
  </TextBlock>
</StackPanel>
</UserControl>

如果您将usercontrol的datacontext设置为self,那么您将断开继承的datacontext,这不是您想要的。所以你必须在你的用户控件中删除所有类型的datacontext设置为self。

编辑:更改IpRangeFields中的代码:没有DataContext! DataContext =&#34; {Binding ElementName = _parent}&#34;并且只需更改Binding to Start,其余由您决定

<UserControl x:Class="Customizing.Views.IpRangeFields"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:ig="http://schemas.infragistics.com/xaml"
         xmlns:local="clr-namespace:Customizing.Views"
         xmlns:net="clr-namespace:System.Net;assembly=System"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:behaviors="clr-namespace:Customizing.Behaviors"
         xmlns:system="clr-namespace:System;assembly=mscorlib"
         mc:Ignorable="d"
         x:Name="Uc"
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
    <system:String x:Key="InputMaskIp">000.000.000.000</system:String>
</UserControl.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="60" />
        <RowDefinition Height="10" />
        <RowDefinition Height="60" />
        <RowDefinition Height="10" />
        <RowDefinition Height="60" />
        <RowDefinition Height="10" />
        <RowDefinition Height="60" />
        <RowDefinition Height="10" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="2.5*" />
    </Grid.ColumnDefinitions>

    <Label Grid.Row="0" Grid.Column="0" Content="Start" VerticalContentAlignment="Center" FontSize="18"
           HorizontalContentAlignment="Center" FontWeight="Bold" />

    <TextBox VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Row="0" Grid.Column="1"
             FontSize="22" Validation.Error="_ValidationError"
             Text="{Binding ElementName=Uc, Path=Start, ValidatesOnNotifyDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
      </TextBox>

</Grid>
</UserControl>

EDIT2:当然你的地址属性ObjectType需要你在这里设置的公共属性:(例如,如果属性在你的Adresses对象中被称为MyStart那么......)

<range:IpRangeFields Start="{Binding MyStart}" End="{Binding End}"
                     Subnet="{Binding Subnet}" Gateway="{Binding Gateway}"/>

答案 1 :(得分:-1)

这是一个常见问题。

您正在使用UserControl上的绑定,该绑定已经由其他内容设置DataContext

相反,您的绑定需要引用父控件DataContext,在本例中是ListBoxItem。

{Binding DataContext.Start,
            RelativeSource={RelativeSource Mode=FindAncestor, 
                                        AncestorType=ListBoxItem}}