0

My problem: I have a listbox with owners of dogs, and i have a listbox with dogs. I want to modify the dogs listbox itemtemplate as the following: DogName(textblock)+DogKind(textblock)+Owners(combobox).The first two was successful, but i cant add the existing owners to the combobox. If i give a name to my combobox like :

<ComboBox x:Name="mycombo" />

i cant see the mycombo variable in the c# code. The XAML:

<Window x:Class="CodeFirst.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sajat="clr-namespace:CodeFirst"
        Title="MainWindow" Height="557.638" Width="721.294"
        >
<Grid x:Name="grid1"> 
<ListBox x:Name="listbox2" HorizontalAlignment="Left" Height="313" Margin="338,10,0,0" VerticalAlignment="Top" Width="250">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">                        
                        <TextBlock Text="{Binding Path=Name}"/>
                        <TextBlock Text=", "/>
                        <TextBlock Text="{Binding Path=Kind}"/>     
                        <ComboBox />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
</ListBox>     
</Grid>
</Window>

How can i give the itemsource to the combobox, or how can i reach to add the owners?

fzl
  • 171
  • 2
  • 10
  • Consider using Converter, do not use 3 textboxes, use 1 only, and change the sole textblock's text with the formatted string or get it use a converter. – David Apr 02 '13 at 16:17

2 Answers2

0

If you use the DataContext, you can set the Binding like this:

<ComboBox ItemsSource="{Binding Path=DataContext.MyItemsSource, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></ComboBox>
Vyacheslav Volkov
  • 4,592
  • 1
  • 20
  • 20
0

First of all, in order to work with WPF or other XAML-based technologies, you must understand that

UI is not Data. Data is Data. UI is UI.

This means that you should not manipulate any ComboBox or any other UI elements in code, in order to populate them with data, but instead create a ViewModel and bind these objects to that.

In this example, the Window itself is used as ViewModel because it's a simple example, but you should consider moving all application logic to a separate class:

<Window x:Class="MiscSamples.UIisNotData"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="UIisNotData" Height="300" Width="300">
    <UniformGrid Rows="1" Columns="2">
        <DockPanel>
            <TextBlock Text="Owners:" DockPanel.Dock="Top" FontWeight="Bold" TextAlignment="Center" Margin="2"/>
            <Button Content="Add" Width="80" DockPanel.Dock="Bottom" Margin="2" Click="AddOwner"/>

            <ListBox ItemsSource="{Binding Owners}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <TextBlock Text="{Binding Name}" x:Name="block"/>
                            <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Visibility="Collapsed" x:Name="box"/>
                        </Grid>
                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem}}" Value="True">
                                <Setter TargetName="block" Property="Visibility" Value="Collapsed"/>
                                <Setter TargetName="box" Property="Visibility" Value="Visible"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </DockPanel>

        <DockPanel>
            <TextBlock Text="Dogs:" DockPanel.Dock="Top" FontWeight="Bold" TextAlignment="Center" Margin="2"/>
            <ListBox ItemsSource="{Binding Dogs}" HorizontalContentAlignment="Stretch">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <DockPanel>
                            <ComboBox ItemsSource="{Binding DataContext.Owners, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
                                      SelectedItem="{Binding Owner}" DisplayMemberPath="Name"
                                      DockPanel.Dock="Right" Width="100"/>
                            <TextBlock>
                                <Run Text="{Binding Name}"/>
                                <Run Text=", "/>
                                <Run Text="{Binding Kind}"/>
                            </TextBlock>
                        </DockPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </DockPanel>
    </UniformGrid>
</Window>

Code Behind (This code should be placed in a ViewModel):

public partial class UIisNotData : Window
    {
        public ObservableCollection<Owner> Owners { get; set; }
        public ObservableCollection<string> Kinds { get; set; }
        public ObservableCollection<Dog> Dogs { get; set; } 

        public UIisNotData()
        {
            InitializeComponent();

            Owners = new ObservableCollection<Owner>
                {
                    new Owner() {Name = "Jack"},
                    new Owner() {Name = "Mike"},
                    new Owner() {Name = "Kirk"},
                    new Owner() {Name = "John"},
                };

            Kinds = new ObservableCollection<string>
                {
                    "Affenpinscher",
                    "Afghan Hound",
                    "Airedale Terrier",
                    "Akita"
                    //.. All the rest of dog Breeds taken from http://www.petmd.com/dog/breeds?breed_list=az#.UVsQKpPcmQo
                };

            Dogs = new ObservableCollection<Dog>
                {
                    new Dog() {Name = "Bobby", Kind = Kinds[0], Owner = Owners[0]},
                    new Dog() {Name = "Fido", Kind = Kinds[1], Owner = Owners[1]},
                    new Dog() {Name = "Toby", Kind = Kinds[2], Owner = Owners[2]}
                };

            DataContext = this;
        }

        private void AddOwner(object sender, RoutedEventArgs e)
        {
            Owners.Add(new Owner(){Name = "New Owner"});
        }
    }

Data Model:

    public class Owner : PropertyChangedBase
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
    }

    public class Dog: PropertyChangedBase
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }

        private Owner _owner;
        public Owner Owner
        {
            get { return _owner; }
            set
            {
                _owner = value;
                OnPropertyChanged("Owner");
            }
        }

        private string _kind;
        public string Kind
        {
            get { return _kind; }
            set
            {
                _kind = value;
                OnPropertyChanged("Kind");
            }
        }
    }

PropertyChangedBase Class:

public class PropertyChangedBase:INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Result:

enter image description here

There are 3 important aspects you need to consider about this example:

  • I am in no way manipulating UI elements in code. That's completely unnecessary most of the time in WPF.
  • The classes from the Data Model implement INotifyPropertyChanged in order to support 2-way binding in WPF.
  • The Collections are of type ObservableCollection<T> in order to support automatic notification when elements are added/removed from the collection (in order to automatically update the ListBoxes, etc).

Another thing you may notice is that the XAML elements in my example have no specific size or Margin values. Things like Margin="338,10,0,0" is usually what you get from the Visual Studio designer and indicates a poorly structured layout. I recommend you look at the Layout elements in WPF (DockPanel, StackPanel, Grid, UniformGrid, WrapPanel, etc), and start coding the XAML yourself instead of using the designer. This will allow a much higher level of scalability and will also save you from the nuances of Fixed-position elements.

Community
  • 1
  • 1
Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154