I have a bunch of buttons being render inside an items control. I want to get access to the data context of the clicked button. How can I achieve that?
Model:
public class RunYear
{
public RunYear(int year)
{
Year = year;
Months = new Month[3];
}
public int Year { get; set; }
public Month[] Months { get; set; }
}
public class Month
{
public int ColumnIndex { get; set; }
public string MonthName { get; set; }
// some other props
}
Code behind:
public partial class MainWindow : Window
{
private ObservableCollection<RunYear> _years = new ObservableCollection<RunYear>();
public ObservableCollection<RunYear> Years { get{return _years; } }
public MainWindow()
{
DataContext = this;
InitializeComponent();
GenerateData();
}
private void GenerateData()
{
for (int i = 2010; i < 2015; i++)
{
var runYear = new RunYear(i);
runYear.Months[0] = new Month() { ColumnIndex = 0, MonthName = $"Jan {i}" };
runYear.Months[1] = new Month() { ColumnIndex = 1, MonthName = $"Feb {i}" };
runYear.Months[2] = new Month() { ColumnIndex = 2, MonthName = $"Mar {i}" };
Years.Add(runYear);
}
}
public void OnClick(object sender, RoutedEventArgs args)
{
// how do I get the databound item?
var cp = sender as ContentPresenter; //doesn't work
var vm = cp?.Content as Month;
}
}
XAML:
<Grid>
<ItemsControl Name="icYears" ItemsSource="{Binding Years}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
</Grid.RowDefinitions>
<DockPanel Grid.Column="0" Grid.Row="0" >
<TextBox IsReadOnly="True" TextAlignment="Center" Text="{Binding Year}" />
</DockPanel>
<ItemsControl Grid.Column="1" Name="icMonths" ItemsSource="{Binding Months}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="75"></RowDefinition>
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Click="OnClick" Content="{Binding MonthName}" Padding="2" Margin="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Do you mean like this?
private void Button_Click(object sender, RoutedEventArgs e)
{
var vm = (sender as FrameworkElement).DataContext;
Related
I'm using Rachel Lim's GridHelper to get dynamic number of rows. What I wanted to achieve is to have each row displayed one below another (done), to be able to resize them (done - using GridSplitter) and to have content resized proportionally to the screen size.
Result:
What I would like to have:
Xaml:
<Grid>
<ItemsControl ItemsSource="{Binding RowSource}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid local:GridHelper.RowCount="{Binding RowCount}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Grid.Row" Value="{Binding RowNumber}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="Col 1" />
<DataGridTextColumn Header="Col 2" />
<DataGridTextColumn Header="Col 3" />
</DataGrid.Columns>
</DataGrid>
<Button Grid.Column="1" Content="Btn" />
</Grid>
<GridSplitter Height="5" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Grid.Row="0" ResizeDirection="Rows" ResizeBehavior="CurrentAndNext"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
ViewModel:
internal class MyViewModel
{
public ObservableCollection<RowInfo> RowSource { get; set; }
public int RowCount { get { return RowSource.Count; } }
public MyViewModel()
{
RowSource = new ObservableCollection<RowInfo>()
{
new RowInfo() { RowNumber = 0 },
new RowInfo() { RowNumber = 1 },
new RowInfo() { RowNumber = 2 }
};
}
}
RowInfo:
public class RowInfo
{
public int RowNumber { get; internal set; }
}
I think your approach is all wrong. You cannot use an ItemsControl, as the GridSplitter items need to be at the ItemsPanel level rather than in the DataTemplate - otherwise, it won't work.
You are better off using a custom behavior on the Grid Itself - see example code below:
public class GridAutoRowChildBehavior : Behavior<Grid>
{
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(GridAutoRowChildBehavior),
new PropertyMetadata(null, OnGridPropertyChanged));
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(GridAutoRowChildBehavior),
new PropertyMetadata(null, OnGridPropertyChanged));
private static void OnGridPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((GridAutoRowChildBehavior) d).ResetGrid();
}
private void ResetGrid()
{
var source = ItemsSource as IEnumerable;
if (source == null || ItemTemplate == null)
return;
AssociatedObject.Children.Clear();
AssociatedObject.RowDefinitions.Clear();
var count = 0;
foreach (var item in source)
{
var content = new ContentPresenter
{
ContentTemplate = ItemTemplate,
Content = item
};
var splitter = new GridSplitter
{
Height = 5,
VerticalAlignment = VerticalAlignment.Bottom,
HorizontalAlignment = HorizontalAlignment.Stretch
};
AssociatedObject.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
Grid.SetRow(content,count);
Grid.SetRow(splitter,count);
AssociatedObject.Children.Add(content);
AssociatedObject.Children.Add(splitter);
count++;
}
}
public DataTemplate ItemTemplate
{
get { return (DataTemplate) GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public object ItemsSource
{
get { return GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
}
Then in your XAML you code it up like this:
<Grid>
<i:Interaction.Behaviors>
<local:GridAutoRowChildBehavior ItemsSource="{Binding RowsSource}">
<local:GridAutoRowChildBehavior.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="Col 1" />
<DataGridTextColumn Header="Col 2" />
<DataGridTextColumn Header="Col 3" />
</DataGrid.Columns>
</DataGrid>
<Button Grid.Column="1" Content="Btn" />
</Grid>
</DataTemplate>
</local:GridAutoRowChildBehavior.ItemTemplate>
</local:GridAutoRowChildBehavior>
</i:Interaction.Behaviors>
</Grid>
I have tested this and it works exactly as you need.
The only additional thing you need to do is add the Nuget package Systems.Windows.Interactivity.WPF to your project
Use star-sizing for the RowDefintions that you created in the GridHelper class:
public static void RowCountChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (!(obj is Grid) || (int)e.NewValue < 0)
return;
Grid grid = (Grid)obj;
grid.RowDefinitions.Clear();
for (int i = 0; i < (int)e.NewValue; i++)
grid.RowDefinitions.Add(
new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) }); //<--
SetStarRows(grid);
}
And set the Height of your first RowDefinition to *:
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Background="Yellow">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="Col 1" />
<DataGridTextColumn Header="Col 2" />
<DataGridTextColumn Header="Col 3" />
</DataGrid.Columns>
</DataGrid>
<Button Grid.Column="1" Content="Btn" />
</Grid>
<GridSplitter Height="5" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Grid.Row="0" ResizeDirection="Rows" ResizeBehavior="CurrentAndNext"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
I have tab control In wpf Application. I want to add dynamic Tab Item having data grid control ? Any Solutions. Thanks In Advance.
please try the next solution:
Xaml Code (just uncomment gridview code if needed):
<Window x:Class="TabControlSOHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tabControlSoHelpAttempt="clr-namespace:TabControlSOHelpAttempt"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<tabControlSoHelpAttempt:TabControlViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TabControl Grid.Row="0" ItemsSource="{Binding Customers}" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type tabControlSoHelpAttempt:Customer}">
<TextBlock>
<Run Text="Customer:"></Run>
<Run Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"></Run>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type tabControlSoHelpAttempt:Customer}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<!--<ListView Grid.Row="1" ItemsSource="{Binding CustomerDataCollection}">
<ListView.View>
<GridView>
<GridViewColumn Header="Book Name" DisplayMemberBinding="{Binding BookName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
<GridViewColumn Header="Keeping From" DisplayMemberBinding="{Binding KeepingFrom, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
</GridView>
</ListView.View>
</ListView>-->
<DataGrid Grid.Row="1" ItemsSource="{Binding CustomerDataCollection}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Book Name" Binding="{Binding BookName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></DataGridTextColumn>
<DataGridTextColumn Header="Keeping From" Binding="{Binding KeepingFrom, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" VerticalAlignment="Bottom" Command="{Binding RemoveCustomer}">Remove Customer</Button>
<Button Grid.Column="1" VerticalAlignment="Bottom" Command="{Binding AddCustomer}">Add Customer</Button></Grid>
</Grid></Window>
View Models and models:
public class TabControlViewModel:BaseObservableObject
{
private ICommand _addCommand;
private Customer _selectedCustomer;
private ICommand _removeCommand;
public TabControlViewModel()
{
//here you can inject a model which will be able
//to support the initial data for the Customers colection,
//and will inform user if a customer was added or removed.
//On each model changes you can add a new custome object into Customers collection,
//since this collection is ObservableCollection, UI will be updated imidiatelly
Customers = new ObservableCollection<Customer>
{
new Customer
{
Name = "John",
CustomerDataCollection = new ObservableCollection<CustomerData>
{
new CustomerData
{
BookName = "Uncle Vania",
KeepingFrom = DateTime.Today,
},
new CustomerData
{
BookName = "Anna Karenine",
KeepingFrom = DateTime.Today,
}
}
},
new Customer
{
Name = "Tom",
CustomerDataCollection = new ObservableCollection<CustomerData>
{
new CustomerData
{
BookName = "War and Peace",
KeepingFrom = DateTime.Today,
},
new CustomerData
{
BookName = "Alice's Adventures in Wonderland",
KeepingFrom = DateTime.Today,
}
}
},
};
}
public ObservableCollection<Customer> Customers { get; set; }
public ICommand AddCustomer
{
get { return _addCommand ?? (_addCommand = new RelayCommand(AddCommandMethod)); }
}
private void AddCommandMethod()
{
Customers.Add(new Customer());
SelectedCustomer = Customers.LastOrDefault();
}
public ICommand RemoveCustomer
{
get { return _removeCommand ?? (_removeCommand = new RelayCommand(RemoveCommandMethod)); }
}
private void RemoveCommandMethod()
{
if(SelectedCustomer == null) return;
Customers.Remove(SelectedCustomer);
SelectedCustomer = Customers.LastOrDefault();
}
public Customer SelectedCustomer
{
get { return _selectedCustomer; }
set
{
_selectedCustomer = value;
OnPropertyChanged();
}
}
}
public class Customer:BaseObservableObject
{
public Customer()
{
Name = "Enter Customer Name";
CustomerDataCollection = new ObservableCollection<CustomerData>();
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public ObservableCollection<CustomerData> CustomerDataCollection { get; set; }
}
public class CustomerData:BaseObservableObject
{
private string _bookName;
private DateTime _qty;
public string BookName
{
get { return _bookName; }
set
{
_bookName = value;
OnPropertyChanged();
}
}
public DateTime KeepingFrom
{
get { return _qty; }
set
{
_qty = value;
OnPropertyChanged();
}
}
}
I'll be glad to help if there will be problems with the code, just let me know about this.
Regards.
I have one part of the view is dynamic based on a TemplateSelector. However, the binding does not work for the controls in the DataTemplate.(The controls do show on screen, just the conetent/texts are empty). I suspect it's a DataContext issue, but couldn't figure out after a lot of searching on line. Here is my XAML:
<Grid>
<Grid.DataContext>
<local:MyViewModel/>
</Grid.DataContext>
<Grid.Resources>
<DataTemplate x:Key="T1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="Music"
Style="{StaticResource ResourceKey=textBlockStyle}" />
<TextBox Grid.Column="1"
Grid.Row="0"
Style="{StaticResource ResourceKey=textBoxStyle}"
Text="{Binding Path=MusicName}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="T2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="Currency"
Style="{StaticResource ResourceKey=textBlockStyle}" />
<ComboBox Grid.Column="1"
Grid.Row="0"
Style="{StaticResource ResourceKey=comboBoxStyle}"
ItemsSource="{Binding Path=Currency_List}"
SelectedItem="{Binding Path=Currency}" />
</Grid>
</DataTemplate>
<local:ProductTypeTemplateSelector T1="{StaticResource ResourceKey=T1}"
T2="{StaticResource ResourceKey=T2}"
x:Key="myTemplateSelector" />
<Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
<RowDefinition Height="40"/>
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<!-- This biding works -->
<TextBlock Grid.Row="0"
Text="{Binding Path=MusicName}"/>
<!-- This biding does not work -->
<ContentControl Grid.Row="1"
Name="ccc"
Content="{Binding Path=Product_Type}"
ContentTemplateSelector="{StaticResource myTemplateSelector}">
</ContentControl>
</Grid>
This is my View Model (Technically, it is View Model and Model mixed together. I am not really implementing a full MVVM pattern)
public class MyViewModel: INotifyPropertyChanged
{
public MyViewModel()
{
SetLists();
}
protected void SetLists()
{
SetList_Product_Type();
SetList_Currency();
}
protected void SearchAndPopulate()
{
string query = string.Format("select * from dbo.vehicle_attributes where ticker like '%{0}%'", Search_Text);
DataView dv = DAL.ExecuteQuery(query);
if (dv.Count > 0)
{
DataRowView dvr = dv[0];
Vehicle_Id = int.Parse(dvr["vehicle_id"].ToString());
Product_Type = dvr["product_type_name"].ToString();
Vehicle_Name = dvr["vehicle_name"].ToString();
Is_Onshore = dvr["domicile_name"].ToString() == "Onshore";
Currency = dvr["currency"].ToString();
CUSIP = dvr["CUSIP"].ToString();
ISIN = dvr["isin"].ToString();
Ticker = dvr["ticker"].ToString();
Valoren = dvr["valoren"].ToString();
PC_Class = PC_Class_List.Find(x => x.Class_Name == dvr["class_name"].ToString());
Implementation_Type = Implementation_Type_List.Find ( x => x.Implementation_Type_Name == dvr["implementation_type_name"].ToString());
Price_Frequency = Price_Frequency_List.Find( x => x.Price_Frequency_Name == dvr["price_freq_name"].ToString());
Status = Status_List.Find( x => x.Status_Name == dvr["status_name"].ToString());
if (!string.IsNullOrEmpty(dvr["last_status_update"].ToString()))
{
Status_Date = DateTime.Parse(dvr["last_status_update"].ToString());
}
else
{
Status_Date = DateTime.MinValue;
}
switch (Product_Type)
{
case "Mutual Fund":
query = string.Format("select lf.dividend_currency, i.ticker from dbo.liquid_funds lf " +
"left join dbo.vehicles i on i.vehicle_id = lf.index_id " +
"where lf.vehicle_id ='{0}'",Vehicle_Id);
DataView dv_mutual_fund = DAL.ExecuteQuery(query);
if(dv_mutual_fund.Count > 0)
{
DataRowView dvr_mutual_fund = dv_mutual_fund[0];
Dividend_Currency = dvr_mutual_fund["dividend_currency"].ToString();
Benchmark_Ticker = dvr_mutual_fund["ticker"].ToString();
}
break;
default:
break;
}
}
}
public ICommand SearchVehicleCommand
{
get
{
return new Command.DelegateCommand(SearchAndPopulate);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
//ProductType
protected List<string> _product_Type_List = new List<string>();
public List<string> Product_Type_List
{
get
{
return _product_Type_List;
}
set
{
_product_Type_List = value;
OnPropertyChanged("Product_Type_List");
}
}
protected void SetList_Product_Type()
{
string query = "SELECT * FROM dbo.product_types WHERE is_enabled = 1";
DataView dv = DAL.ExecuteQuery(query);
List<string> l = new List<string>();
for (int i = 0; i < dv.Count; i++)
{
l.Add(dv[i]["product_type_name"].ToString());
}
Product_Type_List = l;
}
protected string _product_type;
public string Product_Type
{
get
{
return _product_type;
}
set
{
_product_type = value;
OnPropertyChanged("Product_Type");
SetList_Implementation_Type();
}
}
//Currency
protected List<string> _currency_List = new List<string>();
public List<string> Currency_List
{
get
{
return _currency_List;
}
set
{
_currency_List = value;
OnPropertyChanged("Currency_List");
}
}
protected void SetList_Currency()
{
string query = "SELECT currency FROM dbo.currencies";
DataView dv = DAL.ExecuteQuery(query);
List<string> l = new List<string>();
for (int i = 0; i < dv.Count; i++)
{
l.Add(dv[i]["currency"].ToString());
}
Currency_List = l;
}
protected string _currency;
public string Currency
{
get
{
return _currency;
}
set
{
_currency = value;
OnPropertyChanged("Currency");
}
}
// Music Name
protected string _musicName;
public string MusicName
{
get
{
return _musicName;
}
set
{
_musicName = value;
OnPropertyChanged("MusicName");
}
}
}
This is the class interface (sorry for the formatting above, but somehow I can't get it right):
And this is my DelegateCommand class:
public class DelegateCommand : ICommand
{
private readonly Action _action;
public DelegateCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
}
This is the DataTemplateSelector:
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate T1 { get; set; }
public DataTemplate T2 { get; set; }
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
string product_type = (string)item;
if (product_type == "Type1")
return T1;
else
return T2;
}
}
The DataContext of a DataTemplate is set to the object that it is bound to. So in your case the DataContext for your Templates are Product_Type and you are expecting it to be MyViewModel.
There is a workaround for what you need. It uses a RelativeSource binding and FindAncester to access the DataContext of the Parent object.
Your DataTemplate bindings should look like this:
XAML
<DataTemplate x:Key="T1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="Music"
Style="{StaticResource ResourceKey=textBlockStyle}" />
<TextBox Grid.Column="1"
Grid.Row="0"
Style="{StaticResource ResourceKey=textBoxStyle}"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentControl}}, Path=DataContext.MusicName}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="T2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="Currency"
Style="{StaticResource ResourceKey=textBlockStyle}" />
<ComboBox Grid.Column="1"
Grid.Row="0"
Style="{StaticResource ResourceKey=comboBoxStyle}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentControl}}, Path=DataContext.Currency_List}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentControl}}, Path=DataContext.Currency}" />
</Grid>
</DataTemplate>
From MSDN
Find Ancestor - Refers to the ancestor in the parent chain of the data-bound element. You can use this to bind to an ancestor of a specific type or its subclasses.
The AncestorType attribute goes up the visual tree and finds the first Ancestor of the given type, in this case it's looking for ContentControl the path to the required property can then be set relative to this.
Update
Think of a template as a guide on how to display an object. The DataContext of the DataTemplate is going to be whatever object it is asked it to display.
In this case the ContentControl is told to display Product_Type and, depending on the value of Product_Type, to use a particular Template. Product_Type is given to the DataTemplate and becomes the DataContext.
WPFTutorials has some good examples.
Data Binding from one textbox to another is not happen properly .Here is the below code am using.
EDIT:
Mainwindow xaml:
<Grid>
<TextBox Name="txtBox1" AcceptsReturn="True" Margin="0,0,203,148" VerticalScrollBarVisibility="Visible" LostFocus="txtBox1_LostFocus" TextChanged="txtBox1_TextChanged">
</TextBox>
<Button Content="ButtonToDisplay" Height="46" HorizontalAlignment="Left" Margin="362,71,0,0" Name="button1" VerticalAlignment="Top" Width="98" Click="button1_Click" />
</Grid>
CheckAddressWindow xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="195"/>
<RowDefinition Height="28"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<GroupBox Name="grpFullName" Header="Name Details" BorderBrush="Black" BorderThickness="1" FontWeight="Bold" >
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition Height="15"/>
<RowDefinition Height="50"/>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition Height="15"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition Width="0.7*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Name="lblstreet" Content="Street" VerticalAlignment="Top"></Label>
<TextBox Grid.Column="1" Name="txtStreet" VerticalAlignment="Stretch" Margin="0,0,0,5" TextChanged="txtStreet_TextChanged" Text="{Binding Path=szStreet Mode=OneWay}"></TextBox>
</Grid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition Width="201"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Name="lblCity" Content="City" VerticalAlignment="Top"></Label>
<TextBox Grid.Column="1" Name="txtCity" VerticalAlignment="Top" Text="{Binding Path=szCityname,Mode=OneWay}"></TextBox>
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition Width="0.7*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Name="lblstate" Content="State/Province" VerticalAlignment="Top"></Label>
<TextBox Grid.Column="1" Name="txtState" VerticalAlignment="Top" Text="{Binding Path=szState}"></TextBox>
</Grid>
<Grid Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition Width="0.7*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Name="lblZip" Content="Zip/PostalCode" VerticalAlignment="Top" Grid.ColumnSpan="2"></Label>
<TextBox Grid.Column="2" Name="txtZip" VerticalAlignment="Top" Text="{Binding Path=iZip}"></TextBox>
</Grid>
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition Width="0.7*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Name="lblCountry" Content="Country/Region" VerticalAlignment="Top"></Label>
<ComboBox Grid.Column="1" Name="cbCountry" VerticalAlignment="Top" IsEditable="True" ></ComboBox>
</Grid>
</Grid>
</GroupBox>
<StackPanel Grid.Row="1" Orientation="Horizontal" FlowDirection="LeftToRight">
<CheckBox Name="chkFullAddress" Margin="5,8,5,5" Checked="CheckedEnabled" Unchecked="UncheckedEnabled" IsChecked="true"></CheckBox>
<Label Name="lblFullname" Content="Show this again when name is incomplete or unclear"></Label>
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button Name="btnFullnameCancel" Content="Cancel" Margin="10,10,10,10" Width="50" Click="btnCheckAddressCancelClick"></Button>
<Button Name="btnFullnameOk" Content="Ok" Margin="10,10,10,10" Width="50" Click="btnCheckAddressOkClick"></Button>
</StackPanel>
</Grid>
Person.cs code:Edited
public string szStreet
{
get ;
set ;
}
public string szCityname
{
get ;
set ;
}
public string szState { get; set; }
public int iZip { get; set; }
public static bool bCheck { get; set; }
public static bool bCheckFullAddress { get; set; }
Mainwindow LostFocusEvent: Edited
private void txtBox1_LostFocus(object sender, RoutedEventArgs e)
{
if ((!string.IsNullOrEmpty(txtBox1.Text)) && (!string.IsNullOrWhiteSpace(txtBox1.Text)))
{
if (Person.bCheckFullAddress)
{
CheckAddressWindow ca = new CheckAddressWindow(txtBox1);
ca.ShowDialog();
}
}
else
{
CheckAddressWindow ca = new CheckAddressWindow(txtBox1);
ca.chkFullAddress.IsChecked = caObj.chkFullAddress.IsChecked;
ca.txtCity.Text = txtBox1.Text;
ca.ShowDialog();
}
}
ButtonClick event:Edited
private void button1_Click(object sender, RoutedEventArgs e)
{
caObj.chkFullAddress.IsChecked = (Person.bCheckFullAddress == true) ? true : false;
if (!string.IsNullOrEmpty(txtBox1.Text.Trim()))
{
CheckAddressWindow ca = new CheckAddressWindow(txtBox1);
ca.chkFullAddress.IsChecked = caObj.chkFullAddress.IsChecked;
ca.ShowDialog();
}
else
{
CheckAddressWindow ca = new CheckAddressWindow(txtBox1);
ca.chkFullAddress.IsChecked = caObj.chkFullAddress.IsChecked;
ca.ShowDialog();
ca.txtCity.Text = txtBox1.Text;
}
}
CheckAddressWindow.cs:Edited
public partial class CheckAddressWindow : Window
{
public static bool bChecked = true;
Person objPerson = new Person();
bool bCheckAddress = true;
TextBox txt = new TextBox();
public CheckAddressWindow()
{
InitializeComponent();
}
public CheckAddressWindow(TextBox txtName)
{
InitializeComponent();
txt = txtName;
StringCollection objSc = new StringCollection();
int iLinecount = txt.LineCount;
for (int iCount = 0; iCount < iLinecount; iCount++)
{
objSc.Add(txt.GetLineText(iCount));
}
if (objSc.Count.Equals(5))
{
objPerson.szStreet = (objSc[0] + objSc[1]).Trim();
objPerson.szCityname = objSc[2].Trim();
objPerson.szState = objSc[3].Trim();
objPerson.iZip = objSc[4].Trim();
}
if (objSc.Count.Equals(3))
{
objPerson.szStreet = (objSc[0] + objSc[1]).Trim();
string[] arrName = objSc[2].Split(',', ' ');
objPerson.szCityname = arrName[0].Trim();
objPerson.szState = arrName[1].Trim();
objPerson.iZip = arrName[2].Trim();
}
}
private void btnCheckAddressCancelClick(object sender, RoutedEventArgs e)
{
this.Close();
}
private void btnCheckAddressOkClick(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(txtCity.Text))
{
//txt.Text = txtStreet.Text + "\n" + txtCity.Text + "\n" + txtState.Text + "\n" + txtZip.Text + "\n" + cbCountry.SelectedValue;
objPerson.szStreet = txtStreet.Text.Trim();
objPerson.szCityname = txtCity.Text.Trim();
objPerson.szState = txtState.Text.Trim();
objPerson.iZip = Convert.ToInt32(txtZip.Text.Trim());
txt.Text = objPerson.szStreet + "\n" +objPerson.szCityname+","+txtState.Text + " " + txtZip.Text;
if (chkFullAddress.IsChecked == true)
{
bCheckAddress = true;
}
else
{
bCheckAddress = false;
}
}
this.Close();
}
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
cbCountry.Items.Add("India");
cbCountry.Items.Add("US");
cbCountry.SelectedIndex = 0;
}
private void txtStreet_TextChanged(object sender, TextChangedEventArgs e)
{
}
private void CheckedEnabled(object sender, RoutedEventArgs e)
{
chkFullAddress.IsChecked = Person.bCheckFullAddress = true;
}
private void UncheckedEnabled(object sender, RoutedEventArgs e)
{
chkFullAddress.IsChecked = Person.bCheckFullAddress = false;
}
}
}
I am trying to bind data or text entered in mainwindow to checkaddresswindowxaml...as soon as the checkAddresswindow loads it shows all the text feilds are null,if i enter some data in checkaddress feild then click ok button it will show mainwindow.In mainWindow ,when click the button it binding and showing the two text box values.My issue for the first time its binding data to checkAddresswindow.How can i achieve this.am new to this concepts.If i did nay mistakes please rectify me.
Data binding is not happen...where i went wrong and which part of code i need to change to achieve it.
When running your app, do you see any Binding Errors in your debug output window?
I am not seeing where you are setting your DataContext for your CheckAddressWindow. I think you cut off the top of your XAML for that file.
Another problem may be that the "Person" object you are binding to in your CheckAddressWindow is not the same "Person" object being used by the MainWindow.
I would recommend that you have a Model that contains a Person object that both the MainWindow and the CheckAddressWindow bind to. This way, both windows are referring to the same person.
For info and tutorials on MVVM architecture for WPF, see the following link:
MVVM: Tutorial from start to finish?
I am beginner in WPF. I am trying bind nested listview from ViewModel.
I have two listview parent and child respectively. In my parent listview datatemplate want bind child listview it's something
like nested gridview in asp.net. Could any one there please help me to sort out this problem.
Data Display like:
StudentID Name
S101 Azad
Math 3
C# 3
Here StudentID,Name parent listview content and subjects are child listview content which is filter by StudentID.
Thanks,
Az#d
<Grid>
<ListView ItemsSource="{Binding Students}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="2"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Grid.Column="0"/>
<TextBlock Text="{Binding RollNo}" Grid.Column="2"/>
<ListView ItemsSource="{Binding SubjectAndMarks}" Grid.Row="2" Grid.ColumnSpan="2" Grid.Column="0" BorderThickness="0">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Subject}" Grid.Column="0"/>
<TextBlock Text="{Binding Marks}" Grid.Column="2"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
public class SubjectAndMarks
{
public string Subject { get; set; }
public double Marks { get; set; }
}
public class Student
{
public string Name { get; set; }
public int RollNo { get; set; }
public ObservableCollection<SubjectAndMarks> SubjectAndMarks { get; set; }
}
public class ViewModel //ViewModel
{
public ObservableCollection<Student> Students { get; set; }
public ViewModel()
{
Students = new ObservableCollection<Student>();
Students.Add(new Student()
{
Name = "Harish",
RollNo = 1,
SubjectAndMarks = new ObservableCollection<SubjectAndMarks>()
{new SubjectAndMarks(){Subject="Maths",Marks=100},new SubjectAndMarks(){Subject="Hindi",Marks=100},
new SubjectAndMarks(){Subject="Science",Marks=100}}
});
Students.Add(new Student()
{
Name = "Pankaj",
RollNo = 2,
SubjectAndMarks = new ObservableCollection<SubjectAndMarks>()
{new SubjectAndMarks(){Subject="Maths",Marks=100},new SubjectAndMarks(){Subject="Hindi",Marks=40},
new SubjectAndMarks(){Subject="Science",Marks=30}}
});
Students.Add(new Student()
{
Name = "Deepak",
RollNo = 3,
SubjectAndMarks = new ObservableCollection<SubjectAndMarks>()
{new SubjectAndMarks(){Subject="Maths",Marks=90},new SubjectAndMarks(){Subject="Hindi",Marks=50},
new SubjectAndMarks(){Subject="Science",Marks=60}}
});
}
}
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
I hope that will help.