I have a UserControl for quantities called QtyControl, with a DependencyProperty called Qty (an int). I'm registering this property with DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, but if I don't put UpdateSourceTrigger=PropertyChanged on the control consumer's binding, it doesn't work and I don't understand why.
The code should allow you to click the Add button and see whatever number you selected in the ComboBox, but always shows 0.
MainWindow.xaml:
<Window x:Class="UserControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UserControlTest"
Title="MainWindow" Height="150" Width="250"
WindowStartupLocation="CenterScreen">
<StackPanel>
<DataGrid ItemsSource="{Binding MyItems}"
IsReadOnly="True"
Height="Auto" Width="Auto"
HeadersVisibility="Column"
AutoGenerateColumns="False"
SelectionMode="Single">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Qty" Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:QtyControl Qty="{Binding QtyRequested}" /> <!--, UpdateSourceTrigger=PropertyChanged - this is needed, but I don't know why when I registered Qty with DefaultUpdateSourceTrigger=PropertyChanged -->
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Add" Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Click="_AddItemBtn_Click">Add</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
MainWindow.xaml.cs:
using System.Windows.Controls;
using System.Windows.Media;
namespace UserControlTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
private void _AddItemBtn_Click(object sender, RoutedEventArgs e)
{
DataGridRow parentRow = _FindDataGridRowFromCl((Control)sender);
MyItem item = (MyItem)parentRow.Item;
MessageBox.Show($"QtyRequested = {item.QtyRequested}");
}
private DataGridRow _FindDataGridRowFromCl(Control cl)
{
for (Visual vi = cl as Visual; vi != null; vi = VisualTreeHelper.GetParent(vi) as Visual)
if (vi is DataGridRow row)
return row;
return null;
}
}
public class MyItem
{
public int QtyRequested { get; set; } = 0;
}
public class MyViewModel : INotifyPropertyChanged
{
private ObservableCollection<MyItem> _myItems;
public ObservableCollection<MyItem> MyItems {
get {
return _myItems;
}
set {
_myItems = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyItems)));
}
}
public MyViewModel()
{
MyItems = new ObservableCollection<MyItem> { new MyItem() };
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
QtyControl.xaml:
<UserControl x:Class="UserControlTest.QtyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UserControlTest"
mc:Ignorable="d" Height="22" Width="42"
>
<Grid>
<ComboBox Name="_comboBox"
SelectedIndex="{Binding Qty, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ComboBox.Items>
<ComboBoxItem Content="0" IsSelected="True" />
<ComboBoxItem Content="1" />
<ComboBoxItem Content="2" />
</ComboBox.Items>
</ComboBox>
</Grid>
</UserControl>
QtyControl.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace UserControlTest
{
public partial class QtyControl : UserControl
{
public QtyControl()
{
InitializeComponent();
}
public static DependencyProperty QtyProperty;
static QtyControl()
{
QtyProperty = DependencyProperty.Register(
"Qty",
typeof(int),
typeof(QtyControl),
new FrameworkPropertyMetadata(
defaultValue: 1,
flags: FrameworkPropertyMetadataOptions.AffectsArrange,
propertyChangedCallback: null,
coerceValueCallback: null,
isAnimationProhibited: false,
defaultUpdateSourceTrigger: UpdateSourceTrigger.PropertyChanged
)
/* Also does not work
new FrameworkPropertyMetadata(
1,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
)
*/
);
}
public int Qty
{
get { return (int)GetValue(QtyProperty); }
set { SetValue(QtyProperty, value); }
}
}
}
Related
I have a select list called Printers which is filled by default, after selecting a value from this select list the list called Trays should be filled but it doesn't. I've tried various methods but it seems to me that I still don't understand what the problem is. In addition, I would like to see a new list after pressing the Add next row button and it would also be full. I tried to use DependencyProperty and INotifyPropertyChanged but I don't think I fully understand the basics of the assumption or my problem is specific.
<UserControl x:Class="TestWPF.User_Controls.TrayItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TestWPF.User_Controls"
mc:Ignorable="d"
Name="ucTray"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="White">
<StackPanel Margin="10" x:Name="spTray" Orientation ="Horizontal" Height="35" HorizontalAlignment="Center">
<Label VerticalAlignment="Center">Trays</Label>
<ComboBox Width="250" VerticalContentAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ElementName=ucTray, Path=Trays }" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label VerticalAlignment="Center">Documents</Label>
<ComboBox Width="250" VerticalContentAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Documents , RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Grid>
namespace TestWPF.User_Controls
{
public partial class TrayItem : UserControl
{
public List<string> Trays
{
get { return (List<string>)GetValue(TraysProperty); }
set { SetValue(TraysProperty, value); }
}
// Using a DependencyProperty as the backing store for Trays. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TraysProperty =
DependencyProperty.Register("Trays", typeof(List<string>), typeof(TrayItem), new PropertyMetadata(null));
public List<string> Documents
{
get { return (List<string>)GetValue(DocumentsProperty); }
set { SetValue(DocumentsProperty, value); }
}
// Using a DependencyProperty as the backing store for Documents. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DocumentsProperty =
DependencyProperty.Register("Documents", typeof(List<string>), typeof(TrayItem), new PropertyMetadata(null));
public TrayItem()
{
InitializeComponent();
}
}
}
<Window x:Class="TestWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWPF"
xmlns:control="clr-namespace:TestWPF.User_Controls"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel Margin="10" x:Name="spItems">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label>Printers</Label>
<ComboBox x:Name="cmbPrinterList" Width="300" SelectionChanged="cmbPrinterList_SelectionChanged" ItemsSource="{Binding MyPrinters}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
<control:TrayItem x:Name="cTray" Trays="{Binding MyTrays, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Documents="{Binding Path = MyDocuments, Mode=TwoWay}">
</control:TrayItem>
</StackPanel>
<Button x:Name="btnAddTray" Margin="10" Width="100" Height="40" Click="btnAddTray_Click" >Add next row</Button>
</Grid>
</Window>
namespace TestWPF
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public List<string> MyPrinters { get; set; }
public string? _SelectedPrinter;
public string? SelectedPrinter
{
get
{
return _SelectedPrinter;
}
set
{
_SelectedPrinter = value;
}
}
private List<string>? _MyTrays;
public List<string>? MyTrays
{
get
{
return _MyTrays;
}
set
{
_MyTrays = value;
RaisePropertyChanged("SelectedPrinter");
RaisePropertyChanged("MyTrays");
RaisePropertyChanged("MyDocuments");
}
}
public List<string>? MyDocuments { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
MyPrinters = new List<string>()
{
"Printer_1",
"Printer_2",
"Printer_3"
};
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void cmbPrinterList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string? value = (string?)cmbPrinterList.SelectedItem;
switch (value)
{
case "Printer_1":
MyTrays = new List<string>()
{
"aaa",
"bbb",
"ccc"
};
MyDocuments = new List<string>()
{
"zzz",
"qqq",
"rrr"
};
break;
case "Printer_2":
MyTrays = new List<string>()
{
"111",
"222",
"333"
};
MyDocuments = new List<string>()
{
"666",
"777",
"888"
};
break;
case "Printer_3":
MyTrays = new List<string>()
{
"1dg",
"b2f",
"bs4"
};
MyDocuments = new List<string>()
{
"b2vg",
"eb5",
"asc"
};
break;
}
SelectedPrinter = value;
}
private void btnAddTray_Click(object sender, RoutedEventArgs e)
{
TrayItem trayItem = new TrayItem { DataContext = cTray.DataContext };
spItems.Children.Add(trayItem);
}
}
}
Can someone help me? I have the folowing XAML code in my MainWindow.xaml file:
<ListBox ItemsSource="{Binding Files}" HorizontalAlignment="Left"
Height="371" Margin="281,53,0,0" VerticalAlignment="Top"
Width="609">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in my ViewModel.cs I have property:
public List<string> Files { get; set; }
But, when I click the button and add some items to Files nothing happens.
P.S. Sorry for my bad English :)
List does not implement INotifyCollectionChanged, instead of List, use ObservableCollection<string>
Additional Info: List vs ObservableCollection vs INotifyPropertyChanged
Here is your solution, just add this code and press 'Add String' button to make it work. I have used 'ObservableCollection' instead of List and made to listen it using 'INotifyPropertyChanged' interface in ViewModel.cs class
MainWindow.xaml
<Window x:Class="ListBox_Strings.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myApp="clr-namespace:ListBox_Strings"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<myApp:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Files}" HorizontalAlignment="Left"
VerticalAlignment="Top" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" Content="Add String" Click="Button_Click"></Button>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace ListBox_Strings
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as ViewModel;
vm.Files.Add("New String");
}
}
}
ViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace ListBox_Strings
{
public class ViewModel:INotifyPropertyChanged
{
private ObservableCollection<string> m_Files;
public ObservableCollection<string> Files
{
get { return m_Files; }
set { m_Files = value;
OnPropertyChanged("Files"); }
}
public ViewModel()
{
Files = new ObservableCollection<string>();
Files.Add("A");
Files.Add("B");
Files.Add("C");
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}
I have a view model with some fields, i.e.
type ViewModel =
member x.a = [1;2;3]
member x.b = [4;5;6]
member x.c = [7]
and in WPF application places some views, as:
<Control.Resources>
<DataTemplate x:Key="ItemTempl">
<TextBlock Text="{Binding}" />
</DataTemplate>
<DataTemplate x:Key="SomeTempl">
<ListBox ItemsSource="{Binding}"
ItemTemplate="{StaticResource ItemTempl}" />
</DataTemplate>
</Control.Resources>
<StackPanel>
<TabControl x:Name="ListBoxViewPresenter">
<TabItem Header="vm.a" Content="{Binding vm.a}"
ContentTemplate="{StaticResource SomeTempl}"/>
<TabItem Header="vm.b" Content="{Binding vm.b}"
ContentTemplate="{StaticResource SomeTempl}"/>
<TabItem Header="vm.c" Content="{Binding vm.c}"
ContentTemplate="{StaticResource SomeTempl}"/>
</TabControl>
<ListBox x:Name="ListBoxViewPresenter">
<ListBoxItem Content="{Binding vm.a}"
ContentTemplate="{StaticResource SomeTempl}" />
<ListBoxItem Content="{Binding vm.b}"
ContentTemplate="{StaticResource SomeTempl}" />
<ListBoxItem Content="{Binding vm.c}"
ContentTemplate="{StaticResource SomeTempl}" />
</ListBox>
</StackPanel>
What should I do to achieve such behavior:
when you clicked on some element in vm.a/b/c in ListBoxViewPresenter so same element in ListBoxViewPresenter is must selected in corresponding TabItem.
UPD:
Specifically my real problem, changing from origin topic.
I have ViewModel with fields: onelines, twolines... and a field with name selected_scheme.
In xaml:
<TreeViewItem Header="{Binding Path=name}" x:Name="ProjectArea">
<TreeViewItem Header="Однониточные планы" Style="{StaticResource MyTreeViewItem}">
<ContentPresenter Content="{Binding onelines}" ContentTemplate="{StaticResource SchemeWrapperTemplate}" />
</TreeViewItem>
<TreeViewItem Header="Двухниточные планы" Style="{StaticResource MyTreeViewItem}">
<ContentPresenter Content="{Binding twolines}" ContentTemplate="{StaticResource SchemeWrapperTemplate}" />
</TreeViewItem>
And data template:
<DataTemplate x:Key="SchemeWrapperTemplate">
<ListBox ItemsSource="{Binding schemes}"
ItemTemplate="{StaticResource SchemeTemplate}"
SelectedItem="{Binding selected_scheme}">
<ListBox.Style>
In other place of the program:
<Grid Grid.Row="1">
<TextBlock Text="{Binding selected_scheme.path}" />
</Grid>
And when you click on some ListBoxes then selected item not changing if you click on a yet SelectedItems.
First of all you should define the ItemsSource of your TabItem and ListBox in the ViewModel they are bound too.
Then you can bind their SelectedItem to a property in your ViewModel.
Here is a code sample (I was too lazy to create a seperate ViewModel class, hence it is mixed with my main window class, but you get the idea...) :
Codebehind :
namespace WpfApplication13
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private object _currentlySelectedItem;
public object CurrentlySelectedItem
{
get { return _currentlySelectedItem; }
set
{
_currentlySelectedItem = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("CurrentlySelectedItem"));
}
}
}
public class MyClass
{
public string MyString { get; set; }
public MyClass(string myString)
{
this.MyString = myString;
}
}
private List<MyClass> _myItemsSource = new List<MyClass>
{
new MyClass("toto"),
new MyClass("tata")
};
public List<MyClass> MyItemsSource
{
get { return _myItemsSource; }
set { _myItemsSource = value; }
}
public object A
{
get { return "toto"; }
}
public object B
{
get { return "tata"; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
}
}
xaml :
<Window x:Class="WpfApplication13.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<TabControl x:Name="ListBoxViewPresenter"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource}" />
<ListBox x:Name="ListBoxViewPresenter2"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource}" />
</StackPanel>
</Grid>
</Window>
New answer after you edited :
It does not make much sense to bind the same object to the SelectedItem property of two different lists that contain different elements. You have to redesign your application or you may be confronted to many problems due to this strange design in the futur.
Still, you can achieve want you want to do with a little codebehing. When the user clicks on one of your ListBox, you set the selected item of your other listbox to null. Then you will be notified when you re-click on the first listbox since the selection goes from null to something.
xaml:
<Window x:Class="WpfApplication13.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<ListBox x:Name="ListBoxViewPresenter"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource}" />
<ListBox x:Name="ListBoxViewPresenter2"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource2}" />
</StackPanel>
</Grid>
</Window>
codebehind:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.ComponentModel;
namespace WpfApplication13
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private object _currentlySelectedItem;
public object CurrentlySelectedItem
{
get { return _currentlySelectedItem; }
set
{
_currentlySelectedItem = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("CurrentlySelectedItem"));
}
}
}
private List<int> _myItemsSource = new List<int> { 1, 2 };
private List<int> _myItemsSource2 = new List<int> { 3, 4 };
public List<int> MyItemsSource
{
get { return _myItemsSource; }
set { _myItemsSource = value; }
}
public List<int> MyItemsSource2
{
get { return _myItemsSource2; }
set { _myItemsSource2 = value; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
ListBoxViewPresenter.PreviewMouseDown += ListBoxViewPresenter_PreviewMouseDown;
ListBoxViewPresenter2.PreviewMouseDown += ListBoxViewPresenter2_PreviewMouseDown;
}
void ListBoxViewPresenter_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ListBoxViewPresenter2.SelectedItem = null;
}
void ListBoxViewPresenter2_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ListBoxViewPresenter.SelectedItem = null;
}
}
}
I think I have stumbled upon a bug in WPF when textboxes in a DataGrid contains invalid data and are disabled.
When the textboxes contain invalid data and are disabled I can make the data (and binding?) magically disappear by clicking twice on the textbox with invalid data, click on another element in the row, then click twice on the textbox with invalid data.
I made a small example which reproduces the problem. If you click twice on 121, one time on 55, then twice on 121 the 121 should disappear (and the binding will stop working?). Can anyone confirm it is a bug or am I doing something stupid?
WPF code:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DataGrid ItemsSource="{Binding Path=Tests}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Test header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox IsEnabled="False" Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Test header 2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox IsEnabled="False" Text="{Binding Path=Text2, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
C# code:
using System;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Test> Tests {get;set;}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Tests = new ObservableCollection<Test>();
Tests.Add(new Test());
}
}
public class Test : INotifyPropertyChanged, IDataErrorInfo
{
public string Text { get; set; }
public string Text2 { get; set; }
public Test()
{
Text = "121";
Text2 = "55";
}
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get { return "WTF!!!"; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
i have application with two Tabs. on first tab placed button which sets the current position in dataGrid1 on second tab. While i won't show a second tab, i can't set current position by button1.
<UserControl x:Class="SilverlightApplication9.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" Background="White">
<sdk:TabControl Height="234" HorizontalAlignment="Left" Margin="52,44,0,0" Name="tabControl1" VerticalAlignment="Top" Width="326">
<sdk:TabItem Header="tabItem1" Name="tabItem1">
<Grid>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="74,44,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</sdk:TabItem>
<sdk:TabItem Header="tabItem2" Name="tabItem2">
<Grid>
<sdk:DataGrid ItemsSource="{Binding strs}" RowBackground="White" AutoGenerateColumns="False" Height="141" HorizontalAlignment="Left" Margin="36,12,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="199">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Binding="{Binding}" CanUserReorder="True" CanUserResize="True" CanUserSort="True" Width="Auto" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
</sdk:TabItem>
</sdk:TabControl>
</Grid>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
namespace SilverlightApplication9
{
public partial class MainPage : UserControl
{
private ObservableCollection<string> _strs = new ObservableCollection<string>();
public ObservableCollection<string> strs { get { return _strs; } set { _strs = value; } }
public MainPage()
{
this.DataContext = this;
InitializeComponent();
strs.Add("1");
strs.Add("2");
strs.Add("3");
strs.Add("4");
strs.Add("5");
}
private void button1_Click(object sender, RoutedEventArgs e)
{
dataGrid1.SelectedIndex = 2;
}
}
}
The problem is that the DataGrid isn't loaded when you attempt to set the SelectedIndex in the button click handler if you haven't already navigated to the tab that contains the DataGrid.
The way to achieve what you want is to use data binding. You will also want to implement INotifyPropertyChanged for any subsequent changes to the property you bind DataGrid.SelectedIndex to. The following is a rough example of how to do what you want in the code you provided.
public partial class MainPage : UserControl, INotifyPropertyChanged
{
private ObservableCollection<string> _strs
= new ObservableCollection<string>();
public ObservableCollection<string> strs
{
get { return _strs; }
set { _strs = value; }
}
public MainPage()
{
this.DataContext = this;
InitializeComponent();
strs.Add("1");
strs.Add("2");
strs.Add("3");
strs.Add("4");
strs.Add("5");
SelectedIndex = 0;
}
private int _selectedIndex;
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
_selectedIndex = value;
var pChanged = PropertyChanged;
if (pChanged != null)
pChanged(this, new PropertyChangedEventArgs("SelectedIndex"));
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
SelectedIndex ++;
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then update your DataGrid definition in xaml to:
<sdk:DataGrid ItemsSource="{Binding strs}"
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}"
RowBackground="White"
AutoGenerateColumns="False"
Height="141"
HorizontalAlignment="Left"
Margin="36,12,0,0"
Name="dataGrid1"
VerticalAlignment="Top"
Width="199">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Binding="{Binding}"
CanUserReorder="True"
CanUserResize="True"
CanUserSort="True"
Width="Auto" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>