C# DataGrid中的WPF组合框

C# DataGrid中的WPF组合框,c#,wpf,mvvm,combobox,datagrid,C#,Wpf,Mvvm,Combobox,Datagrid,这是一个显示我有问题的行为的例子。我有一个datagrid,它绑定到viewmodel中可观察到的记录集合。在datagrid中,我有一个DataGridTemplateColumn,其中包含一个组合框,该组合框是从viewmodel中的列表填充的。datagrid还包含文本列。窗口底部有一些文本框显示记录内容 <Window x:Class="Customer.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/x

这是一个显示我有问题的行为的例子。我有一个datagrid,它绑定到viewmodel中可观察到的记录集合。在datagrid中,我有一个DataGridTemplateColumn,其中包含一个组合框,该组合框是从viewmodel中的列表填充的。datagrid还包含文本列。窗口底部有一些文本框显示记录内容

<Window x:Class="Customer.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Customer"
    Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <local:SelectedRowConverter x:Key="selectedRowConverter"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="8*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <DataGrid x:Name="dgCustomers" AutoGenerateColumns="False"
                  ItemsSource="{Binding customers}" SelectedItem="{Binding SelectedRow,
                    Converter={StaticResource selectedRowConverter}, Mode=TwoWay}"
                  CanUserAddRows="True" Grid.Row="0" >
            <DataGrid.Columns>
                <DataGridTemplateColumn Width="Auto" Header="Country">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="cmbCountry" ItemsSource="{Binding DataContext.countries,
                                RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                                      DisplayMemberPath="name" SelectedValuePath="name" Margin="5"
                                      SelectedItem="{Binding DataContext.SelectedCountry,
                                RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=TwoWay,
                                UpdateSourceTrigger=PropertyChanged}" SelectionChanged="cmbCountry_SelectionChanged" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="Name" Binding="{Binding name}" Width="1*"/>
                <DataGridTextColumn Header="Phone" Binding="{Binding phone}" Width="1*"/>
            </DataGrid.Columns>
        </DataGrid>

        <Grid x:Name="grdDisplay" DataContext="{Binding ElementName=dgCustomers}" Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="2" Content="Country:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            <Label Grid.Column="4" Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            <BulletDecorator  Grid.Column="0">
                <BulletDecorator.Bullet>
                    <Label Content="Name:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
                </BulletDecorator.Bullet>
                <TextBox x:Name="txtId" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.name}" Margin="5,5,5,5"/>
            </BulletDecorator>
            <BulletDecorator Grid.Column="1">
                <BulletDecorator.Bullet>
                    <Label Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
                </BulletDecorator.Bullet>
                <TextBox x:Name="txtCode" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.countryCode}" Margin="5,5,5,5"/>
            </BulletDecorator>
            <BulletDecorator Grid.Column="2">
                <BulletDecorator.Bullet>
                    <Label  Content="Phone:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
                </BulletDecorator.Bullet>
                <TextBox x:Name="txtPhone" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.phone}" Margin="5,5,5,5"/>
            </BulletDecorator>
        </Grid>
    </Grid>
</Window>
记录类:

public class Record : ViewModelBase
{
    private string _name;
    public string name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("name");
        }
    }

    private string _phone;
    public string phone
    {
        get { return _phone; }
        set
        {
            _phone = value;
            OnPropertyChanged("phone");
        }
    }

    private int _countryCode;
    public int countryCode
    {
        get { return _countryCode; }
        set
        {
            _countryCode = value;
            OnPropertyChanged("countryCode");
        }
    }
}
国家级:

public class Country : ViewModelBase
{
    private string _name;
    public string name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("name");
        }
    }

    private int _id;
    public int id
    {
        get { return _id; }
        set
        {
            _id = value;
            OnPropertyChanged("id");
        }
    }

    private int _code;
    public int code
    {
        get { return _code; }
        set
        {
            _code = value;
            OnPropertyChanged("code");
        }
    }

    public override string ToString()
    {
        return _name;
    }
}
网格模型:

public class GridModel : ViewModelBase
{
    public ObservableCollection<Record> customers { get; set; }
    public List<Country> countries { get; set; }
    public GridModel()
    {
        customers = new ObservableCollection<Record>();
        countries = new List<Country> { new Country { id = 1, name = "England", code = 44 }, new Country { id = 2, name = "Germany", code = 49 },
        new Country { id = 3, name = "US", code = 1}, new Country { id = 4, name = "Canada", code = 11 }};
    }

    private Country _selectedCountry;
    public Country SelectedCountry
    {
        get
        {
            return _selectedCountry;
        }
        set
        {
            _selectedCountry = value;
            _selectedRow.countryCode = _selectedCountry.code;
            OnPropertyChanged("SelectedRow");
        }
    }

    private Record _selectedRow;
    public Record SelectedRow
    {
        get
        {
            return _selectedRow;
        }
        set
        {
            _selectedRow = value;
            Debug.Print("Datagrid selection changed"); 
            OnPropertyChanged("SelectedRow");
        }
    }
}
ViewModelBase:

public class ViewModelBase : INotifyPropertyChanged
{
    public ViewModelBase()
    {

    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}
谢谢你的帮助

编辑谢谢你的帮助标记,我正在运行你在下面的答案中提供的代码,但是我仍然没有在窗口底部的文本框中获得国家代码。我发现以下错误:

System.Windows.Data错误:23:无法使用默认转换将“{NewItemPlaceholder}”从类型“NamedObject”转换为类型“CustomerFreezable.RecordViewModel”,用于“en US”区域性;考虑使用转换器的绑定属性。NotSupportedException:'System.NotSupportedException:TypeConverter无法从MS.Internal.NamedObject转换。 在System.ComponentModel.TypeConverter.GetConvertFromExceptionObject值处 位于System.ComponentModel.TypeConverter.ConvertFromitTypeDescriptor上下文、文化信息文化、对象值 在MS.Internal.Data.DefaultValueConverter.ConvertHelperObject o中,类型destinationType,DependencyObject targetElement,CultureInfo区域性,布尔值isForward'

System.Windows.Data错误:7:ConvertBack无法转换值“{NewItemPlaceholder}”类型“NamedObject”。BindingExpression:Path=SelectedRow;DataItem='GridModel'HashCode=62992796;目标元素为“DataGrid”Name='dgCustomers';目标属性为“SelectedItem”类型“Object”NotSupportedException:“System.NotSupportedException:TypeConverter无法从MS.Internal.NamedObject转换。 在MS.Internal.Data.DefaultValueConverter.ConvertHelperObject o中,键入destinationType、DependencyObject targetElement、CultureInfo区域性、Boolean isForward 在MS.Internal.Data.ObjectTargetConverter.ConvertBackObject o中,类型类型、对象参数、文化信息文化 位于System.Windows.Data.BindingExpression.ConvertBackHelperIValueConverter,对象值,类型sourceType,对象参数,CultureInfo区域性' 数据网格选择已更改 数据网格选择已更改

System.Windows.Data错误:40:BindingExpression路径错误:在“object”RecordViewModel“HashCode=47081572”上找不到“countryCode”属性。BindingExpression:Path=SelectedItem.countryCode;DataItem='DataGrid'名称='dgCustomers';目标元素是“TextBox”Name='txtCode';目标属性为“文本”类型“字符串”

System.Windows.Data错误:23:无法使用默认转换将“{NewItemPlaceholder}”从类型“NamedObject”转换为类型“CustomerFreezable.RecordViewModel”,用于“en US”区域性;考虑使用转换器的绑定属性。NotSupportedException:'System.NotSupportedException:TypeConverter无法从MS.Internal.NamedObject转换。 在System.ComponentModel.TypeConverter.GetConvertFromExceptionObject值处 位于System.ComponentModel.TypeConverter.ConvertFromitTypeDescriptor上下文、文化信息文化、对象值 在MS.Internal.Data.DefaultValueConverter.ConvertHelperObject o中,类型destinationType,DependencyObject targetElement,CultureInfo区域性,布尔值isForward'

System.Windows.Data错误:7:ConvertBack无法转换值“{NewItemPlaceholder}”类型“NamedObject”。BindingExpression:Path=SelectedRow;DataItem='GridModel'HashCode=62992796;目标元素为“DataGrid”Name='dgCustomers';目标属性为“SelectedItem”类型“Object”NotSupportedException:“System.NotSupportedException:TypeConverter无法从MS.Internal.NamedObject转换。 在MS.Internal.Data.DefaultValueConverter.ConvertHelperObject o中,键入destinationType、DependencyObject targetElement、CultureInfo区域性、Boolean isForward 在MS.Internal.Data.ObjectTargetConverter.ConvertBackObject o中,类型类型、对象参数、文化信息文化 位于System.Windows.Data.BindingExpression.ConvertBackHelperIValueConverter,对象值,类型sourceType,对象参数,CultureInfo区域性' 数据网格选择已更改

System.Windows.Data错误:40:BindingExpression路径错误:在“object”RecordViewModel“HashCode=47081572”上找不到“countryCode”属性。BindingExpression:Path=SelectedItem.countryCode;DataItem='DataGrid'名称='dgCustomers';目标元素是“TextBox”Name='txtCode';目标属性为“文本”类型“字符串”

我试图通过更改静态资源来解决BindingExpression路径错误:

<local:BindingProxy x:Key="CountryProxy" Data="{Binding}" />
以及文本框的绑定:

<TextBox x:Name="txtCode" Text="{Binding Path=record.countryCode}" Margin="5,5,5,5"/>

这消除了错误40,但我仍然没有在文本框中看到任何东西。你能告诉我出了什么问题吗?

请原谅我的诚实,但这段代码有很多问题

首先,有一些严重的偏差 来自MVVM。MVVM是一个分层架构…首先是模型,然后是视图模型,然后是视图模型。从技术上讲,转换器是视图的一部分,但如果有的话,它们位于视图的另一侧,而不是视图模型。您所做的是使用转换器在您的模型中生成新记录:

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
    if (value is Record)
        return value;
    return new Customer.Record(); <<<<<<<< this here
}
任何时候,当转换器直接与非视图类一起工作时,都很好地表明您的视图模型没有正常工作,并且几乎总是导致绑定中断和错误行为

另一个问题是,您的Record类看起来像是要作为模型的,也就是说,因为它有一个用于country的整数代码,而不是对country类实例的引用。然而,该类是从ViewModelBase派生的,并且不执行属性更改通知。此外,GridModel中类型为Country(即SelectedCountry)的一个字段将被所有记录绑定,因此更改一个字段的国家代码将更改所有记录

但要回答您的特定问题,问题是DataGrid在检测到其中一个字段已被编辑之前不会创建新记录。在这种情况下,您与SelectedRow的绑定不在记录本身中,因此没有创建记录,也没有传播值

下面是一个更好地坚持MVVM的固定版本,并修复了绑定问题:

// record model
public class Record
{
    public string name {get; set;}
    public string phone { get; set; }
    public int countryCode {get; set;}
}

// record view model
public class RecordViewModel : ViewModelBase
{
    private Record record = new Record();

    public string name
    {
        get { return record.name; }
        set
        {
            record.name = value;
            RaisePropertyChanged("name");
        }
    }

    public string phone
    {
        get { return record.phone; }
        set
        {
            record.phone = value;
            RaisePropertyChanged("phone");
        }
    }

    private Country _country;
    public Country country
    {
        get { return _country; }
        set
        {
            _country = value;
            record.countryCode = value.code;
            RaisePropertyChanged("country");
        }
    }

}

public class Country : ViewModelBase
{
    private string _name;
    public string name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("name");
        }
    }

    private int _id;
    public int id
    {
        get { return _id; }
        set
        {
            _id = value;
            RaisePropertyChanged("id");
        }
    }

    private int _code;
    public int code
    {
        get { return _code; }
        set
        {
            _code = value;
            RaisePropertyChanged("code");
        }
    }

    public override string ToString()
    {
        return _name;
    }
}

public class GridModel : ViewModelBase
{
    public ObservableCollection<RecordViewModel> customers { get; set; }
    public List<Country> countries { get; set; }

    public GridModel()
    {
        customers = new ObservableCollection<RecordViewModel>();
        countries = new List<Country> { new Country { id = 1, name = "England", code = 44 }, new Country { id = 2, name = "Germany", code = 49 },
    new Country { id = 3, name = "US", code = 1}, new Country { id = 4, name = "Canada", code = 11 }};
    }

    private RecordViewModel _selectedRow;
    public RecordViewModel SelectedRow
    {
        get
        {
            return _selectedRow;
        }
        set
        {
            _selectedRow = value;
            Debug.Print("Datagrid selection changed");
            RaisePropertyChanged("SelectedRow");
        }
    }
}

// this is needed for when you need to bind something that isn't part of the visual tree (i.e. your combobox dropdowns)
// see http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ for details
public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
以及XAML:

<Window.Resources>
    <local:BindingProxy x:Key="CountryProxy" Data="{Binding Path=countries}" />
</Window.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="8*"/>
        <RowDefinition Height="3*"/>
    </Grid.RowDefinitions>
    <DataGrid x:Name="dgCustomers" AutoGenerateColumns="False"
          ItemsSource="{Binding customers}" SelectedItem="{Binding SelectedRow, Mode=TwoWay}"
          CanUserAddRows="True" Grid.Row="0" >
        <DataGrid.Columns>
            <DataGridComboBoxColumn Header="Country"
                ItemsSource="{Binding Source={StaticResource ResourceKey=CountryProxy}, Path=Data}" DisplayMemberPath="name"
                SelectedItemBinding="{Binding country, UpdateSourceTrigger=PropertyChanged}" /> 
            <DataGridTextColumn Header="Name" Binding="{Binding name, UpdateSourceTrigger=PropertyChanged}" Width="1*" />
            <DataGridTextColumn Header="Phone" Binding="{Binding phone, UpdateSourceTrigger=PropertyChanged}" Width="1*"/>
        </DataGrid.Columns>
    </DataGrid>

    <Grid x:Name="grdDisplay" Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
        </Grid.ColumnDefinitions>
        <Label Grid.Column="2" Content="Country:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
        <Label Grid.Column="4" Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
        <BulletDecorator  Grid.Column="0">
            <BulletDecorator.Bullet>
                <Label Content="Name:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            </BulletDecorator.Bullet>
            <TextBox x:Name="txtId" Text="{Binding Path=SelectedRow.name}" Margin="5,5,5,5"/>
        </BulletDecorator>
        <BulletDecorator Grid.Column="1">
            <BulletDecorator.Bullet>
                <Label Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            </BulletDecorator.Bullet>
            <TextBox x:Name="txtCode" Text="{Binding Path=SelectedRow.country.code}" Margin="5,5,5,5"/>
        </BulletDecorator>
        <BulletDecorator Grid.Column="2">
            <BulletDecorator.Bullet>
                <Label  Content="Phone:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            </BulletDecorator.Bullet>
            <TextBox x:Name="txtPhone" Text="{Binding Path=SelectedRow.phone}" Margin="5,5,5,5"/>
        </BulletDecorator>
    </Grid>
</Grid>

忘记转换器吧,你不需要它。这段代码引入的一个问题是,您现在需要单击组合框两次:首先选择行,然后再次编辑它。但是网络上有很多地方展示了如何解决这个问题,所以我将把它留给你。

任何批评都是非常有建设性的:我正在运行你发送的代码,但我仍然没有在窗口底部的文本框中看到countryCode。输出窗口中有几个错误;我已经编辑了问题以添加它们。抱歉,国家代码的绑定不正确,我在上面的XAML中修复了它。我注意到的另一件事是,底部的字段直接绑定到元素。虽然这通常是可行的,但是如果您绑定到视图模型字段,您会发现以后的问题会少一些,我现在将代码改为这样做,特别是当您开始移动XAML或需要在代码中放置断点以确保控件正常工作时。最后一件事…底部的国家代码编辑字段当前正确显示国家代码,但编辑它不会传播回记录,这基本上是在datagrid中使用组合框的副作用,但下面是国家代码。如果确实需要此功能,则需要将countryCode字段添加到视图模型中。这就提出了一个问题,视图模型首先是如何获得国家的数组的……这可以通过让GridModel监视新的RecordViewModels被添加到ObservableCollection并在创建时初始化它们来实现。doh……RecordViewModel的国家设置器中也有一个小错误:我在打电话RaisePropertyChanged在countryCode上而不是在country上。它可以工作!太棒了,谢谢你,马克。底部的那些框只是为了让我能够看到发生了什么,它们没有出现在我的实际应用程序中。还感谢您为我设置了正确的MVVM实现路径:
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
    if (value is Record)
        return value;
    return new Customer.Record(); <<<<<<<< this here
}
// record model
public class Record
{
    public string name {get; set;}
    public string phone { get; set; }
    public int countryCode {get; set;}
}

// record view model
public class RecordViewModel : ViewModelBase
{
    private Record record = new Record();

    public string name
    {
        get { return record.name; }
        set
        {
            record.name = value;
            RaisePropertyChanged("name");
        }
    }

    public string phone
    {
        get { return record.phone; }
        set
        {
            record.phone = value;
            RaisePropertyChanged("phone");
        }
    }

    private Country _country;
    public Country country
    {
        get { return _country; }
        set
        {
            _country = value;
            record.countryCode = value.code;
            RaisePropertyChanged("country");
        }
    }

}

public class Country : ViewModelBase
{
    private string _name;
    public string name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("name");
        }
    }

    private int _id;
    public int id
    {
        get { return _id; }
        set
        {
            _id = value;
            RaisePropertyChanged("id");
        }
    }

    private int _code;
    public int code
    {
        get { return _code; }
        set
        {
            _code = value;
            RaisePropertyChanged("code");
        }
    }

    public override string ToString()
    {
        return _name;
    }
}

public class GridModel : ViewModelBase
{
    public ObservableCollection<RecordViewModel> customers { get; set; }
    public List<Country> countries { get; set; }

    public GridModel()
    {
        customers = new ObservableCollection<RecordViewModel>();
        countries = new List<Country> { new Country { id = 1, name = "England", code = 44 }, new Country { id = 2, name = "Germany", code = 49 },
    new Country { id = 3, name = "US", code = 1}, new Country { id = 4, name = "Canada", code = 11 }};
    }

    private RecordViewModel _selectedRow;
    public RecordViewModel SelectedRow
    {
        get
        {
            return _selectedRow;
        }
        set
        {
            _selectedRow = value;
            Debug.Print("Datagrid selection changed");
            RaisePropertyChanged("SelectedRow");
        }
    }
}

// this is needed for when you need to bind something that isn't part of the visual tree (i.e. your combobox dropdowns)
// see http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ for details
public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
<Window.Resources>
    <local:BindingProxy x:Key="CountryProxy" Data="{Binding Path=countries}" />
</Window.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="8*"/>
        <RowDefinition Height="3*"/>
    </Grid.RowDefinitions>
    <DataGrid x:Name="dgCustomers" AutoGenerateColumns="False"
          ItemsSource="{Binding customers}" SelectedItem="{Binding SelectedRow, Mode=TwoWay}"
          CanUserAddRows="True" Grid.Row="0" >
        <DataGrid.Columns>
            <DataGridComboBoxColumn Header="Country"
                ItemsSource="{Binding Source={StaticResource ResourceKey=CountryProxy}, Path=Data}" DisplayMemberPath="name"
                SelectedItemBinding="{Binding country, UpdateSourceTrigger=PropertyChanged}" /> 
            <DataGridTextColumn Header="Name" Binding="{Binding name, UpdateSourceTrigger=PropertyChanged}" Width="1*" />
            <DataGridTextColumn Header="Phone" Binding="{Binding phone, UpdateSourceTrigger=PropertyChanged}" Width="1*"/>
        </DataGrid.Columns>
    </DataGrid>

    <Grid x:Name="grdDisplay" Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
        </Grid.ColumnDefinitions>
        <Label Grid.Column="2" Content="Country:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
        <Label Grid.Column="4" Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
        <BulletDecorator  Grid.Column="0">
            <BulletDecorator.Bullet>
                <Label Content="Name:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            </BulletDecorator.Bullet>
            <TextBox x:Name="txtId" Text="{Binding Path=SelectedRow.name}" Margin="5,5,5,5"/>
        </BulletDecorator>
        <BulletDecorator Grid.Column="1">
            <BulletDecorator.Bullet>
                <Label Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            </BulletDecorator.Bullet>
            <TextBox x:Name="txtCode" Text="{Binding Path=SelectedRow.country.code}" Margin="5,5,5,5"/>
        </BulletDecorator>
        <BulletDecorator Grid.Column="2">
            <BulletDecorator.Bullet>
                <Label  Content="Phone:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            </BulletDecorator.Bullet>
            <TextBox x:Name="txtPhone" Text="{Binding Path=SelectedRow.phone}" Margin="5,5,5,5"/>
        </BulletDecorator>
    </Grid>
</Grid>