I'm learning WPF, I want to pass data from some textboxes to code:
<Grid Margin="0,0,10,43" Name="dataFrm">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Column="0">Name</Label>
<Label Grid.Column="0" Grid.Row="1">Code</Label>
<Label Grid.Column="0" Grid.Row="2">Categry Id</Label>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtName" Text="{Binding Path=Name, UpdateSourceTrigger=LostFocus}" Width="162" Margin="0,0,0,10" HorizontalAlignment="Left"></TextBox>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="txtCode" Text="{Binding Path=Code, UpdateSourceTrigger=LostFocus}" Width="162" Margin="0,0,0,10" HorizontalAlignment="Left"></TextBox>
<TextBox Grid.Row="2" Grid.Column="1" x:Name="txtCatId" Text="{Binding Path=CategoryId, UpdateSourceTrigger=LostFocus}" Width="162" Margin="0,0,0,10" HorizontalAlignment="Left"/>
<Button Grid.Row="3" Grid.Column="1" x:Name="btnSave" Click="btnSave_Click" Width="142" Content="Save"/>
</Grid>
When I click save, I want to cast the retrieved data to a predefined class:
private void btnSave_Click(object sender, RoutedEventArgs e)
{
var productVm = (ClsProducts)dataFrm.DataContext;
MessageBox.Show(productVm.Name);
}
ClsProducts:
public class ClsProducts
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public int CategoryId { get; set; }
}
but the DataContext of dataFrm is null!
Make sure that you set the DataContext property of the Grid or its parent window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ClsProducts();
}
}
Related
I am trying to display an object in a grid using data binding. For some reason Only one Row of the grid is being displayed per entry. I know the data binding works, because if I comment out the first row of each grid it then displays the second row. Below is a sample of the relevant code, as well as a screenshot displaying the issue.
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Game_Tracker.ViewModels;assembly=Game Tracker"
x:Class="Game_Tracker.MainPage">
<ContentPage.BindingContext>
<local:OpponentItemVm/>
</ContentPage.BindingContext>
<StackLayout>
<ListView x:Name="OpponentList" ItemsSource="{Binding OpponentItems }">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame BorderColor="Black"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Padding="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="Name: "/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding OpponentFullName}"/>
<Label Grid.Row="1" Grid.Column="0" Text="Email: "/>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding OpponentEmail}"/>
<Label Grid.Row="2" Grid.Column="0" Text="Phone: "/>
<Label Grid.Row="2" Grid.Column="1" Text="{Binding OpponentPhone}"/>
<Label Grid.Row="3" Grid.Column="0" Text="Address: "/>
<Label Grid.Row="3" Grid.Column="1" Text="{Binding OpponentAddress}"/>
<Label Grid.Row="4" Grid.Column="0" Text="Id: "/>
<Label Grid.Row="4" Grid.Column="1" Text="{Binding OpponentId}"/>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Game_Tracker
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
}
Opponent.cs
using System;
namespace Game_Tracker.Models
{
public class Opponent
{
public int OpponentId { get; set; }
public string OpponentFName { get; set; }
public string OpponentLName { get; set; }
public string OpponentAddress { get; set; }
public string OpponentPhone { get; set; }
public string OpponentEmail { get; set; }
public string OpponentFullName => $"{OpponentFName} {OpponentLName}";
public Opponent(int opponentId, string opponentFName, string opponentLName,
string opponentAddress, string opponentPhone, string opponentEmail)
{
this.OpponentId = opponentId;
this.OpponentFName = opponentFName;
this.OpponentLName = opponentLName;
this.OpponentAddress = opponentAddress;
this.OpponentPhone = opponentPhone;
this.OpponentEmail = opponentEmail;
}
}
}
OpponentItemVM.cs
using System.Collections.ObjectModel;
using Game_Tracker.Models;
namespace Game_Tracker.ViewModels
{
public class OpponentItemVm
{
public ObservableCollection<Opponent> OpponentItems { get; set; }
public OpponentItemVm()
{
OpponentItems = new ObservableCollection<Opponent>();
OpponentItems.Add(new Opponent(1, "Jim", "Jimson",
"123 Fake Street", "(123)123-4567", "Jim#Fake.com" ));
OpponentItems.Add(new Opponent(2, "Ken", "Kensington",
"123 False Street", "(123)123-4567", "Ken#Fake.com" ));
}
}
}
To be honest, I agree with Jason.However, the best practice is that you just add HasUnevenRows to True and let unset the RowHeight property which will automatically set the row height to fit content like below:
<ListView x:Name="OpponentList" ItemsSource="{Binding OpponentItems }" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame BorderColor="Black"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Padding="0"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="Name: "/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding OpponentFullName}"/>
<Label Grid.Row="1" Grid.Column="0" Text="Email: "/>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding OpponentEmail}"/>
<Label Grid.Row="2" Grid.Column="0" Text="Phone: "/>
<Label Grid.Row="2" Grid.Column="1" Text="{Binding OpponentPhone}"/>
<Label Grid.Row="3" Grid.Column="0" Text="Address: "/>
<Label Grid.Row="3" Grid.Column="1" Text="{Binding OpponentAddress}"/>
<Label Grid.Row="4" Grid.Column="0" Text="Id: "/>
<Label Grid.Row="4" Grid.Column="1" Text="{Binding OpponentId}"/>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Official reference docs:
https://learn.microsoft.com/en-us/dotnet/api/xamarin.forms.listview.hasunevenrows?view=xamarin-forms
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/customizing-list-appearance#row-height
I have a counter which increments depending on a foreach Loop from:
public partial class UserControlCounter : UserControl, INotifyPropertyChanged
{
private int _scanStatusCounter;
public int ScanStatusCounter
{
get { return _scanStatusCounter; }
set { _scanStatusCounter = value; NotifyPropertyChanged(); }
}
public UserControlCounter()
{
InitializeComponent();
DataContext = this;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(getAll);
scanStatus.Text = "Persons " + ScanStatusCounter.ToString();
}
private async void getAll()
{
//grab data and iterate it
string[] string_array = new string[] { "a", "b", "c", "d", "e" }; // type = System.String[]
foreach (var i in string_array)
{
ScanStatusCounter++;
await Task.Delay(100);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and the Xaml:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Grid.Column="0" Name="myListBox" Height="40" ></ListBox>
<TextBlock Grid.Row="0" Grid.Column="1" Name="scanStatus" Height="40" Text="{Binding Path=ScanStatusCounter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBlock>
<Button Grid.Row="0" Grid.Column="2" Click="Button_Click" Height="40">Click Me</Button>
</Grid>
That works fine and increments "live", but my problem is that I need to get that now running inside a more complex Data Template.
I've tried to inject the Counter onClick with:
private async void Button_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
var contentPresenter = (btn.TemplatedParent as ContentPresenter);
var ppStatusCounter = contentPresenter.ContentTemplate.FindName("scanStatus", contentPresenter) as TextBlock;
ppStatusCounter.Text = "Entrys found: " + ScanStatusCounter.ToString();
}
But then I get the Value only onClick not like a live incrementing Counter, that's my Data Template:
<UserControl.Resources>
<c1:NameList x:Key="NameListData"/>
<DataTemplate x:Key="NameItemTemplate">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Domaine"/>
<TextBox Grid.Row="1" Grid.Column="0" x:Name="txtDomainName" Text="{Binding Path=DomaineName}" Margin="0,0,5,0"></TextBox>
<Button Grid.Row="1" Grid.Column="1" Width="100" Height="30" Margin="5,5,5,5" x:Name="btnNames" Click="Button_Click" Content="Start Scan" />
<Grid Grid.Column="2" Grid.Row="1" Height="20">
<ProgressBar x:Name="pbStatus" Height="20" Width="100" Minimum="0" Maximum="100" Visibility="Hidden"/>
<TextBlock x:Name="pbStatusText" HorizontalAlignment="Center" VerticalAlignment="Center" Text="Scanning" Visibility="Hidden"/>
</Grid>
<TextBlock Grid.Column="3" Grid.Row="1" Name="scanStatus" Text="{Binding Path=ScanStatusCounter, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="top">
<StackPanel Orientation="Horizontal" Margin="0,20,0,0">
<ListBox x:Name="lstDomainNames"
Margin="5,5,5,5"
ItemsSource="{Binding Source={StaticResource NameListData}}"
ItemTemplate="{StaticResource NameItemTemplate}"
IsSynchronizedWithCurrentItem="True"/>
</StackPanel>
</StackPanel>
</Grid>
that's my Data Source Class, not much there:
public partial class NameList : ObservableCollection<SetCredentials>
{
private static Logger logger = LogManager.GetCurrentClassLogger();
public NameList() : base()
{
using var forest = Forest.GetCurrentForest();
Forest currentForest = Forest.GetCurrentForest();
DomainCollection domains = currentForest.Domains;
foreach (Domain objDomain in domains)
{
Add(new SetCredentials(objDomain.ToString()));
}
}
}
public class SetCredentials
{
private string domainName;
public SetCredentials(string domain)
{
this.domainName = domain;
}
public string DomaineName
{
get { return domainName; }
set { domainName = value; }
}
}
Do I have to add a second Data Binding source to my ItemSource or do I need another approach for this, for example in my TextBox Binding?
I want to make layout with table on page. Right now I have:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Margin="10,0,10,0">
<ListView
x:Name="NewsList"
HorizontalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Margin="15" Grid.Column="0" FontSize="25" Text="{Binding news_time}"/>
<TextBlock Margin="15" Grid.Column="1" FontSize="25" Text="{Binding title}"/>
<TextBlock Margin="15" Grid.Column="2" FontSize="25" Text="{Binding news_text}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</Grid>
I want to make binding for my ObservableCollection objects to add Grid rows dynamically to StackPanel tags. Here is my .cs file.
public class News
{
public int id;
public string news_time;
public string title;
public string news_text;
}
public sealed partial class TermsAndNews : Page
{
public ObservableCollection<News> newsCollection { get; set; } =
new ObservableCollection<News>();
// ...
// Method called on initialization
private async void GetData()
{
var httpClient = new HttpClient();
var responsetNews = await httpClient.GetAsync("http://localhost:3000/news");
responsetNews.EnsureSuccessStatusCode();
string jsonNews = await responsetNews.Content.ReadAsStringAsync();
this.newsCollection = JsonConvert.DeserializeObject<ObservableCollection<News>>(jsonNews);
NewsList.ItemsSource = newsCollection;
}
}
After running the code, nothing is displayed. Any ideas ?
Try changing
this.newsCollection = .....
to
ObservableCollection<News> tempobcol = ...
then iterating through and adding to newsCollection
Foreach (News n in tempobcol)
{ newsCollection.Add(n); }
and finally, removing
NewsList.ItemsSource = newsColleciton;
in order to add it to the XAML
<ListView x:Name:"NewsList" ItemsSource:="{Binding newsCollection}"...../>
Is there any way to get the index of the data which is binded to the ItemsControl, when a button is clicked?
<ItemsControl x:Name="ic_schranke" DataContext="{Binding Schranke}" >
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Height="80" x:Name="btn_open" Click="btn_open_Click" HorizontalAlignment="Stretch" Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="30*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Rectangle x:Name="rect" Grid.ColumnSpan="3" Grid.Row="1" Opacity="0.65" Grid.Column="0" Fill="#FFCEEAFF"/>
<Border CornerRadius="25" Height="50" Width="50" HorizontalAlignment="Center" Grid.Column="0" Grid.Row="1">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name" Grid.Column="1" Grid.Row="1" Text="{Binding name}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="ExtraLight" Foreground="Black" />
</Grid>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the sample data which is binded to the IC:
List<Schranke> elements;
public UserPage()
{
this.InitializeComponent();
elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { name = $"Schranke NR:{i}" });
this.ic_schranke.ItemsSource = elements;
}
And here is the code in the button click event:
private async void btn_open_Click(object sender, RoutedEventArgs e)
{
Grid grid = (sender as Button).Content as Grid;
//Debug.WriteLine($"content {tb.Text} clicked");
var tmp_background = grid.Background;
grid.Background = new SolidColorBrush(new Windows.UI.Color() { A = 255, R = 78, G = 170, B = 44 });
VibrationDevice testVibrationDevice = VibrationDevice.GetDefault();
testVibrationDevice.Vibrate(System.TimeSpan.FromMilliseconds(150));
await Task.Delay(3000);
grid.Background = tmp_background;
}
Maybe something along the lines of this:
the presenter - put this in the data context
public class SchrankePresenter : INotifyPropertyChanged
{
private List<Schranke> _elements;
public List<Schranke> Elements
{
get { return _elements; }
set
{
_elements = value;
OnPropertyChanged("Elements");
}
}
public ICommand ClickCommand { get; set; }
private void OnPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
public SchrankePresenter()
{
var elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { Name = $"Schranke NR:{i}" });
Elements = elements;
ClickCommand = new DelegateCommand(ClickAction);
}
public void ClickAction(Schranke item)
{
VibrationDevice.GetDefault().Vibrate(TimeSpan.FromMilliseconds(150));
}
}
public class Schranke
{
public string Name { get; set; }
}
the template:
<ListView ItemsSource="{Binding Elements}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Height="80"
HorizontalAlignment="Stretch"
Command="{Binding ClickCommand}"
Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="30*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Rectangle x:Name="rect"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Fill="#FFCEEAFF"
Opacity="0.65" />
<Border Grid.Row="1"
Grid.Column="0"
Width="50"
Height="50"
HorizontalAlignment="Center"
CornerRadius="25">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name"
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="ExtraLight"
Foreground="Black"
Text="{Binding Name}" />
</Grid>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
you can do the background animation using a storyboard/visualstate
EDIT : regarding the DelegateCommand, it's a simple implementation I use for my WPF apps -
internal class DelegateCommand<T> : ICommand
{
private Action<T> _clickAction;
public DelegateCommand(Action<T> clickAction)
{
_clickAction = clickAction;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_clickAction((T)parameter);
}
}
I'm learning WPF and I'm trying to do something simple. I have two classes: Candy and MyColor. The code of these two classes look like this
public class Candy
{
public MyColor Color { get; set; }
public string Name { get; set; }
}
public class MyColor
{
public string Name { get; set; }
public uint Id { get; set; }
}
(I have attached an image below to make it clearer)
I have an area in the window in which I can create a MyColor by using a textbox that inserts the MyColor.Name, and a simple logic which increments the MyColor.Id. On the other side of the window, I have a button that creates new item in a ItemsControl which holds Candy. Within this ItemsControl there is an ComboBox which I can specify Candy.Color and a TextBox which I specify the Candy.Name. Finally, when I hit the button Generate List the code should output in the TextBox below a list in the format of
Candy.Color Candy.Name
I'm trying to figure out how to automatically populate the ComboBox filled with a list of Colors I have created so I can specify the Candy color, but I don't know how to bind my data source. Also, how would I generate the text?
Currently my code looks like this
namespace QuestionToAsk
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<MyColor> Colors;
ObservableCollection<Candy> Candies;
public MainWindow()
{
InitializeComponent();
Colors = new ObservableCollection<MyColor>();
Candies = new ObservableCollection<Candy>();
Colors.Add(new MyColor() { Name = "(Unspecified)", Id = 0 });
icColors.ItemsSource = Colors;
icCandies.ItemsSource = Candies;
}
private void btnColor(object sender, RoutedEventArgs e)
{
if (txtColor.Text != "")
{
uint last_id = Colors.Last<MyColor>().Id;
Colors.Add(new MyColor() { Name = txtColor.Text, Id = last_id+1 });
txtColor.Text = "";
}
}
private void btnNewCandy(object sender, RoutedEventArgs e)
{
Candies.Add(new Candy());
}
private void btnGetList(object sender, RoutedEventArgs e)
{
//How to create the list of <Color, Name>?
}
}
public class Candy
{
public MyColor Color { get; set; }
public string Name { get; set; }
}
public class MyColor
{
public string Name { get; set; }
public uint Id { get; set; }
}
}
And my XML file looks like this:
<Window x:Class="QuestionToAsk.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:QuestionToAsk"
Title="Color Candy Maker" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Margin="3">
<Button Content="Add Color" Click="btnColor" DockPanel.Dock="Bottom"/>
<TextBox x:Name="txtColor" DockPanel.Dock="Bottom"/>
<ItemsControl x:Name="icColors" Grid.Column="0" Grid.Row="0" DockPanel.Dock="Top">
<ItemsControl.ItemTemplate>
<DataTemplate x:Name="tColorsTemplate">
<TextBlock Text="{Binding Name}" Name="Color" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<DockPanel Grid.Column="1" Grid.Row="0" Margin="3">
<Grid DockPanel.Dock="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Content="New Candy" Click="btnNewCandy" Grid.Column="0"/>
<Button Content="Generate List" Click="btnGetList" Grid.Column="1"/>
</Grid>
<ItemsControl Name="icCandies" DockPanel.Dock="Top">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox Name="cmbColors" Grid.Column="0">
<!-- How to bind this cmbColors to icColors? -->
</ComboBox>
<TextBox Text="{Binding Name}" Grid.Column="1" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<TextBox x:Name="txtColorCandy"/>
</DockPanel>
</Grid>
</Window>
EDIT
I have removed all my ideas and implemented it using your hard design :D.
The xaml code
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Margin="3">
<Button Content="Add Color" Click="btnColor" DockPanel.Dock="Bottom"/>
<TextBox x:Name="txtColor" DockPanel.Dock="Bottom"/>
<ItemsControl x:Name="icColors" Grid.Column="0" Grid.Row="0" DockPanel.Dock="Top">
<ItemsControl.ItemTemplate>
<DataTemplate x:Name="tColorsTemplate">
<TextBlock Text="{Binding Name}" Name="Color" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<DockPanel Grid.Column="1" Grid.Row="0" Margin="3">
<Grid DockPanel.Dock="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Content="New Candy" Click="btnNewCandy" Grid.Column="0"/>
<Button Content="Generate List" Click="btnGetList" Grid.Column="1"/>
</Grid>
<ItemsControl Name="icCandies" DockPanel.Dock="Top">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox Name="cmbColors" Grid.Column="0"
ItemsSource="{Binding Colors, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
DisplayMemberPath="Name"
SelectedItem="{Binding Color}">
</ComboBox>
<TextBox Text="{Binding Name}" Grid.Column="1" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<TextBox x:Name="txtColorCandy" VerticalScrollBarVisibility="Auto"/>
</DockPanel>
</Grid>
Note the usage of RelativeSource since the values of ComboBox was in a property of the Window class while the selected value was to be saved in the Candy class. So the ItemsSource is bound to a property of Window class and the SelectedItem is bound to a property of the Candy class
Code Behind
public partial class MainWindow : Window
{
private ObservableCollection<MyColor> _colors;
public IEnumerable<MyColor> Colors
{
get { return _colors; }
}
private ObservableCollection<Candy> _candies;
public IEnumerable<Candy> Candies
{
get { return _candies; }
}
public MainWindow()
{
InitializeComponent();
_colors = new ObservableCollection<MyColor>();
_candies = new ObservableCollection<Candy>();
_colors.Add(new MyColor { Name = "(Unspecified)", Id = 0 });
icColors.ItemsSource = Colors;
icCandies.ItemsSource = Candies;
}
private void btnColor(object sender, RoutedEventArgs e)
{
if (txtColor.Text != "")
{
uint last_id = Colors.Last<MyColor>().Id;
_colors.Add(new MyColor() { Name = txtColor.Text, Id = last_id + 1 });
txtColor.Text = "";
}
}
private void btnNewCandy(object sender, RoutedEventArgs e)
{
_candies.Add(new Candy());
}
private void btnGetList(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (var item in _candies)
{
if (item.Name == null || item.Color == null)
continue;
sb.AppendLine(item.Color.Name + " " + item.Name);
}
txtColorCandy.Text = sb.ToString();
}
}
It there are any confusion let me know and I will try to help