I have a data grid with Static Data and two of the items are disabled. The only issue is with Silverlight when using the arrow keys to navigate thru the data grid items the disabled items also get focused on.
I have created a custom data grid class and referenced it in the XAML, and then when using the OnKeyDown event I check for IsEnabled True or False. but so far it is not getting the correct values and I think it is because Where I set the IsEnabled status I am referencing the default Datagrid class?
CustomDataGrid
public class CustomGrid : DataGrid
{
protected override void OnKeyDown(KeyEventArgs e)
{
if(IsEnabled != false)
base.OnKeyDown(e);
}
}
Xaml
<UserControl.Resources>
<Style x:Key="DataGridStyle1" TargetType="local:CustomGrid">
<Setter Property="RowStyle">
<Setter.Value>
<Style TargetType="sdk:DataGridRow">
<Setter Property="IsEnabled" Value="{Binding enabled}"/> //CheckIsEnabled Value
</Style>
</Setter.Value>
</Setter>...
<local:CustomGrid x:Name="McDataGrid" HorizontalAlignment="Left" Height="500" VerticalAlignment="Top" Width="400" Style="{StaticResource DataGridStyle1}"/>
List Data
private List<Model> Data()
{
list.Add(new Model(1, "Test", "1", true));
list.Add(new Model(2, "Ger", "2", true));
list.Add(new Model(3, "dsg", "3", true));
list.Add(new Model(4, "Hd", "4", false));
list.Add(new Model(5, "TeHRFdgst", "5", false));
return list;
}
public class Model : INotifyPropertyChanged
{
public bool _enabled;
public int Id { get; set; }
public string Name { get; set; }
public string Prop { get; set; }
public bool enabled
{
get { return _enabled; }
set
{
_enabled = value;
OnPropertyChanged("enabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public Model(int id, string name, string prop, bool isenabled)
{
Id = id;
Name = name;
Prop = prop;
enabled = isenabled;
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
When I change this <Style TargetType="sdk:DataGridRow"> part with "local:CustomGrid" my entire grid just go blank not to sure why.
Is there any advice on how to accomplish this or maybe a different method?
Try something like this:
public class CustomGrid : DataGrid
{
private List<Model> models;
public CustomGrid()
{
Loaded += (s,e) => models = ItemsSource as List<Model>;
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (models == null)
return;
Model model = CurrentItem as Model;
if (model == null)
return;
int index = models.IndexOf(model);
switch (e.Key)
{
case Key.Down:
//is the next model disabled?
if (index < models.Count - 1 && !models[index + 1].enabled)
e.Handled = true;
break;
case Key.Up:
if (index > 0 && !models[index - 1].enabled)
e.Handled = true;
break;
}
}
}
XAML:
<UserControl ...>
<UserControl.Resources>
<Style x:Key="DataGridStyle1" TargetType="local:CustomGrid">
<Setter Property="RowStyle">
<Setter.Value>
<Style TargetType="sdk:DataGridRow">
<Setter Property="IsEnabled" Value="{Binding enabled}"/>
</Style>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<local:CustomGrid x:Name="McDataGrid" Style="{StaticResource DataGridStyle1}" />
</Grid>
</UserControl>
Related
I created a WPF application where I create a list of items to be executed, as a Treeview. On a click event, I parse the ObservableCollection items one by one. This observableCollection is set as the DataContext for the treeview. When running the tests, I want to highlight the current running item in the Treeview.
I have the implemented following code, but the highlighting on the Treeview (visuallY) does not seem to happen. I checked that the "IsSelected" property does get set/unset as programmed.
I am not sure were I went wrong. Could you point out where the mistake is.
I have this class used as a DataContext to the TreeView (named mainTree).
class mytreefile : INotifyPropertyChanged
{
private string _name { get; set; }
public ObservableCollection <mytreefile> children { get; set; }
bool? _isSelected = false;
public bool? IsSelected
{
get { return _isSelected; }
set { SetIsSelected(value); }
}
void SetIsSelected(bool? val)
{
_isSelected = val;
}
public mytreefile(string value)
{
_name = value;
children = new ObservableCollection<mytreefile>();
}
void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
The XAML file is
<Grid.Resources>
<ResourceDictionary>
<HierarchicalDataTemplate x:Key="tvTemplate" ItemsSource="{Binding children, Mode=TwoWay}">
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{Binding _name, Mode=TwoWay}" Margin="2,0" />
</StackPanel>
</HierarchicalDataTemplate>
</ResourceDictionary>
</Grid.Resources>
<TreeView x:Name="mainTree" Grid.Row="0" Grid.Column="0" Grid.RowSpan="4" Background="WhiteSmoke"
Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Margin="1,0,2,0" SelectedItemChanged="mainTree_SelectedItemChanged"
ItemTemplate="{StaticResource tvTemplate}"
ItemsSource="{Binding}" DataContext="{Binding nodes}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter Property="FontWeight" Value="Normal" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
And my MainWindow code is:
public partial class MainWindow : Window
{
ObservableCollection<mytreefile> nodes = new ObservableCollection<mytreefile>();
mytreefile mtf = null;
Thread thThread = null;
int gnCount = 0;
private void LoadTree ()
{
mytreefile tf1 = new mytreefile("Group1");
nodes.Add(tf1);
mytreefile subtf1 = new mytreefile("Sub Item 1");
mytreefile subtf2 = new mytreefile("Sub Item 2");
mytreefile subtf3 = new mytreefile("Sub Item 3");
mytreefile subtf4 = new mytreefile("Sub Item 4");
tf1.children.Add(subtf1); tf1.children.Add(subtf2); tf1.children.Add(subtf3); tf1.children.Add(subtf4);
maintree.DataContext = nodes;
}
private void OnButton1_click()
{
mtf = nodes.ElementAt(0);
gnCount = 0;
thThread = new Thread(new ThreadStart(this.myThread));
thThread.Start();
}
public void myThread ()
{
for (int i = 0; i < 3; i++)
{
Thread.Sleep(1000);
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Send,
new Action(() => SetTreeItem(i)));
}
}
public void SetTreeItem(int i)
{
if (gnCount > 0) {
mytreefile mtreeitem = mtf.children.ElementAt(gnCount-1);
mtreeitem.IsSelected = false;
}
mytreefile mtreeitem = mtf.children.ElementAt(gnCount++);
mtreeitem.IsSelected = true;
}
}
The problem was with the "mytreefile" class.
The below class works fine. The way the "IsSelected" implementation was done made the difference. Posting the code for reference.
class mytreefile : INotifyPropertyChanged
{
private string _name { get; set; }
public ObservableCollection <mytreefile> children { get; set; }
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (value != this._isSelected)
{
this._isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
}
public mytreefile(string value)
{
_name = value;
children = new ObservableCollection<mytreefile>();
}
void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I have a view model that presents items in ListBox. There are two collection: Source contains all element and Checked contains only checked elements. There are two buttons SelectAll and ClearAll. When I push on one of this button View Model works well and both Source and Checked collections updating, but no changes in Listbox happens.
CheckItemPresenterVM<T> - an element that save the state of one button and implements INotifyPropertyChange, but when property IsChecked changes CollectionChangedevent doesn't reise.
The question is how to make it work?
<UserControl.Resources>
<ItemsPanelTemplate x:Key="listPanelTemplate">
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<Style TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ListBoxItemIsCheckedBinding"
TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected"
Value="{Binding IsChecked, Mode=TwoWay}" />
<Setter Property="Padding"
Value="5,3" />
<Setter Property="Background"
Value="LightCyan"></Setter>
</Style>
<Style TargetType="Button">
<Setter Property="Padding"
Value="7, 3" />
<Setter Property="Margin"
Value="1" />
</Style>
<Style TargetType="ToggleButton">
<Setter Property="Padding"
Value="7, 3" />
<Setter Property="Margin"
Value="1" />
</Style>
</UserControl.Resources>
<Grid>
<Expander Header="Categories"
IsExpanded="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<WrapPanel Grid.Row="0"
Margin="0, 5">
<Button Command="{Binding SelectAll}">Select All</Button>
<Button Command="{Binding ClearAll}">Clear All</Button>
<Button Command="{Binding InvertSelection}">Invert Selection</Button>
</WrapPanel>
<ListBox Grid.Row="1" />
<ListBox Name="CategoriesListBox"
Grid.Row="2"
SelectionMode="Multiple"
ItemsSource="{Binding CategoryVM.Source, Mode=TwoWay}"
ItemContainerStyle="{DynamicResource ListBoxItemIsCheckedBinding}"
ItemsPanel="{StaticResource listPanelTemplate}" />
<ListBox Name="CheckedListBox"
Grid.Row="3"
SelectionMode="Multiple"
ItemsSource="{Binding CategoryVM.Checked}"/>
</Grid>
</Expander>
</Grid>
class TestViewModel
{
public CheckItemVM<string> CategoryVM { get; set; }
public TestViewModel()
{
logger.FileName = "TestSelectionFilter.txt";
logger.AddEventRecord(this);
CategoryVM = new CheckItemVM<string>(
new List<string>() {
"01 elem",
"02 elem",
"03 elem",
"04 elem",
"05 elem",
"06 elem",
"07 elem",
"08 elem",
"09 elem",
"10 elem",
"11 elem",
"12 elem",
"13 elem",
"14 elem",
"15 elem",
"16 elem",
"17 elem",
"18 elem",
"19 elem",
"20 elem",
"21 elem",
"22 elem",
"23 elem",
"24 elem",
"25 elem"});
}
public ICommand SelectAll
{
get
{
return new RelayCommand
{
ExecuteAction = a =>
{
CategoryVM.SelectAll();
},
CanExecutePredicate = p =>
{
return !CategoryVM.IsAllSelected();
}
};
}
}
public ICommand InvertSelection
{
get
{
return new RelayCommand
{
ExecuteAction = a =>
{
CategoryVM.InvertSelection();
},
CanExecutePredicate = p =>
{
return true;
}
};
}
}
public ICommand ClearAll
{
get
{
return new RelayCommand
{
ExecuteAction = a =>
{
CategoryVM.ClearAll();
},
CanExecutePredicate = p =>
{
return CategoryVM.Checked.Any();
}
};
}
}
class CheckItemVM<T>
{
protected ObservableCollection<CheckItemPresenterVM<T>> _checked = new ObservableCollection<CheckItemPresenterVM<T>>();
public ObservableCollection<CheckItemPresenterVM<T>> Source { get; set; }
public ObservableCollection<CheckItemPresenterVM<T>> Checked { get => _checked; }
public CheckItemVM(ICollection<T> _source)
{
Source = new ObservableCollection<CheckItemPresenterVM<T>>();
UpdateSource(_source);
}
protected void UpdateSource(ICollection<T> _source)
{
foreach (var item in _source)
{
Source.Add(new CheckItemPresenterVM<T>(ref _checked)
{ Item = item, IsChecked = false });
}
}
public void SetSelection(CheckItemPresenterVM<T> sourceItem, bool flag)
{
if (sourceItem.IsChecked != flag)
{
sourceItem.IsChecked = flag;
}
}
public void SelectAll()
{
foreach (var item in Source)
{
item.IsChecked = true;
}
}
public void ClearAll()
{
foreach (var item in Source)
{
item.IsChecked = false;
}
}
public void InvertSelection()
{
foreach (var item in Source)
{
if (item.IsChecked) item.IsChecked = false;
else item.IsChecked = true;
}
}
public bool IsAllSelected()
{
return Source.Count == Checked.Count;
}
}
class CheckItemPresenterVM<T> : INotifyPropertyChanged
{
protected bool _isChecked;
protected ObservableCollection<CheckItemPresenterVM<T>> _checked;
public CheckItemPresenterVM(ref ObservableCollection<CheckItemPresenterVM<T>> Checked)
{
_checked = Checked;
}
public T Item { get; set; }
public string Name { get; set; }
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked = value;
if (value)
{
if (!_checked.Contains(this))
{
_checked.Add(this);
NotifyPropertyChanged("Item was Checked");
}
}
else
{
if (_checked.Contains(this))
{
_checked.Remove(this);
NotifyPropertyChanged("Item Unchecked");
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public override string ToString()
{
return Item.ToString();
}
}
I see strange part of you code.
Guessing this fix in PropertyChanged raising call
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked = value;
if (value)
{
if (!_checked.Contains(this))
{
_checked.Add(this);
NotifyPropertyChanged("IsChecked");
}
}
else
{
if (_checked.Contains(this))
{
_checked.Remove(this);
NotifyPropertyChanged("IsChecked");
}
}
}
}
NotifyPropertyChanged fires PoropertyChanged Event with a string parameter you passing to. Meanwhile Binding will receive it.
So, here's 2 friends
<Setter Property="IsSelected" Value="{Binding IsChecked, Mode=TwoWay}" />
and
NotifyPropertyChanged("IsChecked");
If you need tune the update behavior of your control, pass UpdateSourceTrigger setting to to the binding, this way:
<Setter Property="IsSelected" Value="{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Additionally: TwoWay is default for the Mode here. You may not declare it.
When binding a View/Control to an ObservableCollection the View/Control will be redrawn when the CollectionChanged event of ObservableCollection is raised.
This occurs when an item is added, removed, reordered or assigned a new instance; but not (as you've probably realised) when an item raises it's PropertyChanged event.
To get your changes to be reflected in the UI you need the PropertyChanged of each Item to raise the CollectionChanged event of the collection.
public class ObservableItemCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
// Call this from the constructor
private void InitialiseItems()
{
CollectionChanged += ContentCollectionChanged;
foreach (T item in Items)
item.PropertyChanged += ReplaceElementWithItself;
}
private void ContentCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
foreach (T item in e.OldItems)
{
item.PropertyChanged -= ReplaceElementWithItself;
}
}
if (e.NewItems != null)
{
foreach (T item in e.NewItems)
{
item.PropertyChanged += ReplaceElementWithItself;
}
}
}
private void ReplaceElementWithItself(object sender, PropertyChangedEventArgs e)
{
var collectionChangedArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
// Call this on the main thread
OnCollectionChanged(collectionChangedArgs);
}
}
This implementation simply raises the CollectionChanged event as a 'Replace' where the same Item is both removed and inserted, at the same index. Beware that it could have unintended consequences if you have anything that actually cares about the CollectionChanged events beyond binding it to the UI; but it is the simplest way I have found to achieve what you are after.
Aloha,
i've got a ListView in my XAML Code and a Grid inside it.
Now there is a snippet of code from my collegue, i need to complete.
The target is, to double the height of the selected Row. The following Snipped double the height of the FIRST row, but not the selected.
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="HeightRequest" Value="60"/>
<Style.Triggers>
<DataTrigger TargetType="Grid" Binding="{Binding Selected}" Value="True">
<Setter Property="HeightRequest" Value="120"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
What do i have to change, that the selected object is that one, that will have the double height? I'm working with xamarin / xamarin forms UWP / Android.
Additional Info: I have to work in MVVM pattern. So i should avoid using traditional events.
If you want to change the height of ViewCell in runtime . You can call cell.ForceUpdateSize(); to update the cell's size .
So we use an TriggerAction to change this value, and tell the ViewCell to update its size.
public class ForceUpdateSizeTriggerAction : TriggerAction<VisualElement>
{
public int HeighRequest { set; get; }
public ForceUpdateSizeTriggerAction() : base()
{
}
protected override void Invoke(VisualElement sender)
{
var parent = sender.Parent;
while (parent != null && !(parent is ViewCell))
{
parent = parent.Parent;
}
if (parent is ViewCell cell)
{
Device.BeginInvokeOnMainThread(() =>
{
sender.HeightRequest = HeighRequest;
cell.ForceUpdateSize();
});
}
}
}
in xaml
<ListView HasUnevenRows="True" ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectItems,Mode=TwoWay}">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="HeightRequest" Value="60"/>
<Style.Triggers>
<DataTrigger TargetType="Grid" Binding="{Binding IsCheck}" Value="True">
<DataTrigger.EnterActions>
<local:ForceUpdateSizeTriggerAction HeighRequest="120" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<local:ForceUpdateSizeTriggerAction HeighRequest="60" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
in Code Behind
ViewMode
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<Model> MyItems { get; set; }
private Model selectItems;
public Model SelectItems
{
get
{
return selectItems;
}
set
{
if (selectItems != value)
{
selectItems = value;
NotifyPropertyChanged();
foreach(Model model in MyItems)
{
if(selectItems==model)
{
model.IsCheck = !model.IsCheck;
}
}
}
}
}
public MyViewModel()
{
SelectItems = null;
MyItems = new ObservableCollection<Model>() {
new Model(){ IsCheck=false},
new Model(){ IsCheck=false},
new Model(){ IsCheck=false},
new Model(){ IsCheck=false},
new Model(){ IsCheck=false},
new Model(){ IsCheck=false},
new Model(){ IsCheck=false},
};
}
Model
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool isCheck;
public bool IsCheck
{
get
{
return isCheck;
}
set
{
if (isCheck != value)
{
isCheck = value;
NotifyPropertyChanged();
}
}
}
private double height;
public double Height
{
get
{
return height;
}
set
{
if (height != value)
{
height = value;
NotifyPropertyChanged();
}
}
}
}
I'm trying to make some CustomControl Textboxes with validation features.
(for example a number only textbox or a zip-code textbox etc.)
It should realized in a .dll library file.
My project contains a CustomControl for the textbox, a class wich handles the validations,
and a ErrMsgGui CustomControl that should show a error message in a TextBlock
(exmp.: Only numbers allowed...)
My problem is that I don't get the TextBlock Text updated when a method in the validation class is called
Is there a way to trigger the PropertyChangeEvent which updates the Textblock text within the validaiton class?
(Im quite new to wpf)
Generic.xaml:
<Style TargetType="{x:Type local:NumTb}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:NumTb}">
<TextBox Background="{TemplateBinding Background}" Text="{Binding Source={StaticResource NumTbVm}, Path=NumTbText, UpdateSourceTrigger=PropertyChanged}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:ErrMsgGui}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ErrMsgGui}">
<TextBlock Text="{ Binding Source={StaticResource val}, Path=ErrMsgGuiText, UpdateSourceTrigger=PropertyChanged}" Background="{TemplateBinding Background}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Validations.cs:
private const string ONLY_NUMBERS_REGEX = #"^[0-9]+$"; //Nur Zahlen
private string _ErrMsgGuiText;
public string ErrMsgGuiText
{
get { return _ErrMsgGuiText; }
set
{
_ErrMsgGuiText = value;
Debug.Print("QueryText: " + value);
OnPropertyChanged("ErrMsgGuiText");
}
}
public object[] onlyNumbers(string s2c, bool output)
{
object[] objRes = new object[2];
bool result = true;
string errMsg = "";
Regex regex = new Regex(ONLY_NUMBERS_REGEX);
if (s2c != null && s2c != "" && !regex.IsMatch(s2c))
{
result = false;
errMsg = "Nur Zahlen sind zulässig";
}
objRes[0] = result;
objRes[1] = errMsg;
if (output == true)
{
ErrMsgGuiText = errMsg;
}
return objRes;
}
public void onlyNumbers(string s2c)
{
onlyNumbers(s2c, true);
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
NumTbViewModel.cs:
Validations val = null;
public NumTbViewModel()
{
val = new Validations();
}
private string _NumTbText;
public string NumTbText
{
get { return _NumTbText; }
set
{
_NumTbText = value;
this.OnPropertyChanged("NumTbText");
val.onlyNumbers(_NumTbText);
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
It looks like the TextBlock source is looking at a static resource for the Validations class and the Validations called in your NumTbViewModel is NOT the same as the static resource. A solution could be to add a property to NumTbViewModel.cs and point your binding to that property so the Validations class instances will be the same. In NumTbViewModel.cs add:
Validations _val;
public Validations Val
{
get { return _val; }
set
{
_val = value;
this.OnPropertyChanged("Val");
}
}
Change your source and path in xaml binding on the TextBlock:
<TextBlock Text="{ Binding Source={StaticResource NumTbVm}, Path=Val.ErrMsgGuiText, UpdateSourceTrigger=PropertyChanged}" Background="{TemplateBinding Background}"/>
Another way:
You could also set the Val property of your NumTbViewModel when you define your static resource like so:
<local:Validations x:Key="val" />
<local:NumTbViewModel x:Key="NumTbVm" Val="{StaticResource val}" />
Doing this you can keep the bindings like you originally had.
I am trying to bind a collection of custom objects to a tree view's ItemSource in WPF with no success.
Here is the MainWindow.xaml:
<Window
x:Class="AoiImageLift.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:AoiImageLift.UI.ViewModels"
Height="500"
Width="500">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<TreeView ItemsSource="{Binding TreeViewViewModel.ProcessList}"/>
</Window>
Here is the App.xaml:
</Application>
</Application.Resources>
<!-- TreeView Style -->
<Style TargetType="{x:Type TreeView}">
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Visible"/>
<Setter Property="SelectedValuePath" Value="Wafer"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<HierarchicalDataTemplate ItemsSource="{Binding ProcessList}">
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock
FontFamily="SegoeUI"
Foreground="MidnightBlue"
Text="{Binding Wafer}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock
Text="{Binding ProcessNumber}"
FontFamily="SegoeUI"
Foreground="MidnightBlue"/>
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
Here is the MainWindowViewModel.cs:
public class MainWindowViewModel : ViewModel
{
private WaferSelectionTreeViewViewModel treeViewViewModel;
public MainWindowViewModel()
{
BackgroundWorker initThread = new BackgroundWorker();
initThread.DoWork += (sender, e) =>
{
e.Result = new SingulationOneTable().GetWaferList();
};
initThread.RunWorkerCompleted += (sender, e) =>
{
TreeViewViewModel = new WaferSelectionTreeViewViewModel(
(List<string>) e.Result);
};
initThread.RunWorkerAsync();
}
public WaferSelectionTreeViewViewModel TreeViewViewModel
{
get { return treeViewViewModel; }
set
{
treeViewViewModel = value;
OnPropertyChanged("TreeViewViewModel");
}
}
}
FYI, this line of code...
e.Result = new SingulationOneTable().GetWaferList();
...simply returns a large list of strings. That list of strings is then passed into the constructor of the WaferSelectionTreeViewViewModel class.
Here is the WaferSelectionTreeViewViewModel.cs:
public class WaferSelectionTreeViewViewModel : ViewModel
{
private ObservableCollection<Process> processList;
public class TreeViewItemBase : ViewModel
{
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set
{
if (value != isSelected)
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return isExpanded; }
set
{
if (value != isExpanded)
{
isExpanded = value;
OnPropertyChanged("IsExpanded");
}
}
}
}
public class Process : TreeViewItemBase
{
private string name;
public Process(string name)
{
this.name = name;
this.Children = new ObservableCollection<string>();
}
public string Name { get { return name; } }
public ObservableCollection<string> Children { get; set; }
}
public WaferSelectionTreeViewViewModel(List<string> waferList)
{
processList = new ObservableCollection<Process>();
List<string> procList = new List<string>();
foreach (string wafer in waferList)
{
procList.Add(wafer.Substring(0, 4));
}
IEnumerable<string> distintProcessList = procList.Distinct();
foreach (string process in distintProcessList)
{
Process newProcess = new Process(process);
List<string> wafersInProcess = waferList.FindAll(
x => x.Substring(0, 4) == process);
foreach (string waferInThisProcess in wafersInProcess)
{
newProcess.Children.Add(waferInThisProcess);
}
}
}
public ObservableCollection<Process> ProcessList
{
get
{
return processList;
}
set
{
processList = value;
OnPropertyChanged("ProcessList");
}
}
}
Can anyone figure out why the items are not showing up in the tree view??
Regards,
Kyle
There are a couple of errors in your bindings. If you run your application from Visual Studio, you'll probably see one or more messages like this in the Output window:
System.Windows.Data Error: 40 : BindingExpression path error: (...)
Firstly, each root item is bound to a Process object from TreeViewViewModel.ProcessList and it's told to display a property called ProcessNumber, but there is no such property, at least not in the code you posted. So I'm guessing the process items are showing up in the TreeView, but they're blank.
Secondly, your HierarchicalItemTemplate says that the list of child items can be found in a property called ProcessList. But each root item is a Process, which doesn't have that property, so no child items are displayed. You probably mean:
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
Now, Process.Children is a simple list of strings, so you don't need to include the <HierarchicalDataTemplate.ItemTemplate> part (and of course, strings don't have the Wafer property that template is looking for).