0

I have a usercontrol that looks like: enter image description here

and the xaml code:

<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>

As you can see on picture, only subnet field is filled with values, why the binding with, for example start, end and gateway does not work? What am I doing wrong

<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>

And the view model and the view is bound to:

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; }
    }
}

and the model

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>

and the code behind:

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);
        }
    }
}
softshipper
  • 32,463
  • 51
  • 192
  • 400
  • Can you paste the ViewModel the control is bound to? – Baldrick May 29 '15 at 08:14
  • Can you also paste the code for the `LocationRange` and Adresses, since this is were the bound properties are? – Kryptos May 29 '15 at 08:28
  • Also add the `IpRange`. For now what I can say is that your `IpRangeViewModel` class does not seem to implements `INotifyPropertyChanged`. Neither your `LocationRange` class. This is an issue since XAML binding need to be notified when the value of a property changes. – Kryptos May 29 '15 at 08:36
  • But why subnet works fine? As you can see on the picture, only on subnet field contains value, why? – softshipper May 29 '15 at 08:40
  • Maybe the problem is not in your view but in the `range:IpRangeFields` user control. Can you paste that code too? – Kryptos May 29 '15 at 08:41
  • Usercontrol iprangefields is pasted. – softshipper May 29 '15 at 08:43
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/79100/discussion-between-kryptos-and-zero-coding). – Kryptos May 29 '15 at 08:50

2 Answers2

1

pls see this answer first: Hair loss and MVVM user controls

and now my copy and paste general answer to wpf usercontrols

if you create a UserControl with Dependency Properties then your binding should always contain some kind of "relative binding" - i always use elementname binding. so your usercontrol binding should look like this.

<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>

if you set the datacontext for your usercontrol to self, then you break the datacontext inheriting and that is not what you want. so you have to remove all kinds of setting the datacontext to self within your usercontrol.

EDIT: change your code in IpRangeFields: no DataContext!!! DataContext="{Binding ElementName=_parent}" and just alter the Binding to Start the rest is up to you

<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: and of course your Addresses Property ObjectType need the public Properties you set here: (eg. if the Property would be Called MyStart in your Adresses object then..)

<range:IpRangeFields Start="{Binding MyStart}" End="{Binding End}"
                     Subnet="{Binding Subnet}" Gateway="{Binding Gateway}"/>
Community
  • 1
  • 1
blindmeis
  • 22,175
  • 7
  • 55
  • 74
  • "always use a relative binding when binding to a DP"... Can you ellaborate on this? – Fredrik May 29 '15 at 08:30
  • How is that relevant to the OP? The only viewmodel is set on the view which is correct. Everything else are just properties. – Kryptos May 29 '15 at 08:32
  • 1
    when you create a "real" usercontrol then you create at least one DependencyProperty in your UserControl. so if i create a DP like "MyText" in my UserControl then i have to create the binding like in my UserControl.xaml. and now where ever i wanna use this Control, i have to do this: – blindmeis May 29 '15 at 08:34
  • @Kryptos for me a UserControl is something i wanna reuse, so i create it with DependencyProperties but without a DataContext. – blindmeis May 29 '15 at 08:36
  • I did `` but it does not work. What make me confuse is, why binding to subnet is working? – softshipper May 29 '15 at 08:36
  • a UserControl with DPs should not have a DataContext. and in the Xaml of this UserControl you need the ElementName Binding, and not in your View wherer you use it. – blindmeis May 29 '15 at 08:38
  • @blindmeis I think you are mistaken with the OP problem. The only user control here (apart from the view) is `range:IpRangeFields` which does have dp. A view does need a view model. So I don't see your point here. – Kryptos May 29 '15 at 08:39
  • oh i misunderstood the OP. so the OP should post the xaml und .cs for this UserControl :) – blindmeis May 29 '15 at 08:40
  • @blindmeis thankyou for clarifying. time to refactor some code.. =). – Fredrik May 29 '15 at 09:04
-1

This is a common problem.

You are using a binding on a UserControl, which already has it's DataContext set by something else.

Instead, your binding needs to reference the parent controls DataContext, in this case is the ListBoxItem.

{Binding DataContext.Start,
            RelativeSource={RelativeSource Mode=FindAncestor, 
                                        AncestorType=ListBoxItem}}
Mike Eason
  • 9,525
  • 2
  • 38
  • 63