Access Datatemplate with name - c#

I've got many TabItems, but only one DataGrid in a DataTemplate.
Now I will access the DataGrid, because I want to change the content...
<Window.Resources>
<DataTemplate x:Key="ContentTabItem">
<DataGrid Grid.Row="1" Name="_uiDataGrid"/>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl Name="_uiTabControl" SelectionChanged="_uiTabControl_SelectionChanged">
<TabItem Name="_uiTabItembla1" Header="bla1" ContentTemplate="{StaticResource ResourceKey=ContentTabItem}"/>
<TabItem Name="_uiTabItembla2" Header="bla2" ContentTemplate="{StaticResource ResourceKey=ContentTabItem}"/>
</TabControl>
</Grid>
I cant access with the name _uiDataGrid

Try something like this to get the datagrid from the content presenters content template :
var contentPresenter = FindVisualChild<ContentPresenter>(_uiTabControl);
var dt = contentPresenter.ContentTemplate;
var datagrid = DataTemplateName.FindName("_uiDataGrid", contentPresenter)
as DataGrid;
For finding the visual child, refer the functions FindVisualChildren() and FindVisualChild() as mentioned in this stackoverflow post.
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj)
where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
public static childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
foreach (childItem child in FindVisualChildren<childItem>(obj))
{
return child;
}
return null;
}

Related

WPF DataGrid TemplateColumn - Scrolling results in strange effects

I have a strange effect in my datagrid. In this sample the datagrid has 200 rows and the first column has a incrementing index. The datagrid has a MaxHeight property, so i see after loading only the first 30 rows.
I can scroll down the 200 rows but i never see in the first column the numbers 30-200 only repeatly 0-29!?! (I checked that the collection has the right values)
If I change the columns from DataGridTemplateColumn to DataGridTextColumn, I see all the values but this is not what i want.
Has someone an idea, why the cell content not show the right value?
Here is my code. This is a reduced sample of a large MVVM project. Please be lenient of this construction.
<Window.Resources>
<local:RowCellConverter x:Key="rcconv" />
<DataTemplate DataType="{x:Type local:BusinessDataGrid}">
<Grid
Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Transparent"
Focusable="False"
Visibility="Visible">
<DataGrid
Name="dataGrid"
Height="700"
MaxHeight="600"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="True"
CanUserResizeRows="False"
CanUserSortColumns="False"
ColumnWidth="*"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
Initialized="dataGrid_Initialized"
ItemsSource="{Binding rows}"
ScrollViewer.CanContentScroll="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionMode="Single"
SelectionUnit="CellOrRowHeader">
<DataGrid.Resources>
<DataTemplate x:Key="MyFieldCell" DataType="DataGridTemplateColumn">
<StackPanel>
<TextBlock Background="LightSalmon">Hallo</TextBlock>
<TextBox
x:Name="TableCell"
DataContext="{Binding RelativeSource={RelativeSource AncestorType=DataGridCell}, Converter={StaticResource rcconv}}"
IsReadOnly="False"
Background="{Binding Path=StateColor}"
Text="{Binding Path=MyValue}" />
</StackPanel>
</DataTemplate>
</DataGrid.Resources>
</DataGrid>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl Name="iControl" ItemsSource="{Binding Path=MainWindow.bFields}" />
</Grid>
And the code behind:
public partial class MainWindow : Window
{
public ObservableCollection<BusinessField> bFields = new ObservableCollection<BusinessField>();
public MainWindow()
{
BusinessDataGrid bdg = new BusinessDataGrid();
foreach (string col in new string[] { "Col1", "Col2", "Col3", "Col4", "Col5", "Col6", })
{
bdg.cols.Add(col);
}
for (int i = 0; i < 200; i++)
{
FieldRow fr = new FieldRow();
foreach(string col in bdg.cols)
{
FieldCell fc = new FieldCell();
fc.MyValue = string.Format("{0:000}{1}", i, col);
fr.cells.Add(fc);
}
bdg.rows.Add(fr);
}
InitializeComponent();
ItemsControl ic = iControl;
ic.ItemsSource = bFields;
bFields.Add(bdg);
}
private void dataGrid_Initialized(object sender, EventArgs e)
{
DataGrid dg = sender as DataGrid;
if (dg != null)
{
BusinessDataGrid bdg = dg.DataContext as BusinessDataGrid;
if (bdg != null)
bdg.OnUIInitialized(dg);
}
}
}
public class BusinessField : INotifyPropertyChanged
{
private PropertyChangedEventHandler propertyChangedEvent;
public void SendPropertyChanged(string propertyName)
{
VerifyCalledOnUIThread();
if (propertyChangedEvent != null)
propertyChangedEvent(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged
{
add
{
VerifyCalledOnUIThread();
propertyChangedEvent += value;
}
remove
{
VerifyCalledOnUIThread();
propertyChangedEvent -= value;
}
}
[Conditional("Debug")]
protected void VerifyCalledOnUIThread()
{
Debug.Assert(Dispatcher.CurrentDispatcher == Dispatcher.CurrentDispatcher, "Call must be made on UI thread.");
}
}
public class BusinessDataGrid:BusinessField
{
public List<string> cols = new List<string>();
public ObservableCollection<FieldRow> rows = new ObservableCollection<FieldRow>();
public void OnUIInitialized(DataGrid datagrid)
{
DataTemplate dt = (DataTemplate)datagrid.Resources["MyFieldCell"];
datagrid.Columns.Clear();
foreach(string col in cols)
{
DataGridTemplateColumn dgtc = new DataGridTemplateColumn()
{
CellTemplate = dt,
Visibility = Visibility.Visible,
Header = col,
SortMemberPath=col,
};
datagrid.Columns.Add(dgtc);
}
datagrid.ItemsSource = rows;
}
}
public class RowCellConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DataGridCell cell = value as DataGridCell;
if((string)parameter == "FieldCell")
{
}
if (cell == null)
return null;
DataGridCellsPresenter dgcp = TreeHelper.GetVisualParent<DataGridCellsPresenter>(cell);
int ci = dgcp.ItemContainerGenerator.IndexFromContainer(cell);
FieldRow fr = cell.DataContext as FieldRow;
if (fr == null)
return null;
object ret = null;
switch((string)parameter)
{
case "StateColor":
ret = fr.cells[ci].StateColor;
break;
case "MyValue":
ret = fr.cells[ci].MyValue;
break;
default:
ret = fr.cells[ci];
break;
}
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class TreeHelper
{
#endregion
public static T GetVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = GetVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
public static T GetVisualParent<T>(DependencyObject child) where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child); //we’ve reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we’re looking for
T parent = parentObject as T;
if (parent != null)
return parent;
else
return GetVisualParent<T>(parentObject);
}
}
public enum FieldCellState
{
Ok,
Error
}
public class FieldRow
{
public ObservableCollection<FieldCell> cells = new ObservableCollection<FieldCell>();
}
public class FieldCell : INotifyPropertyChanged
{
public string Colname;
private string myValue;
public override string ToString()
{
return MyValue;
}
public string MyValue
{
get { return myValue; }
set { myValue = value; }
}
public FieldCellState MyState
{
get { return (MyValue.Contains("7")) ? FieldCellState.Error : FieldCellState.Ok; }
}
public Brush StateColor
{
get { return (MyState == FieldCellState.Ok) ? new SolidColorBrush(Colors.LightGreen) : new SolidColorBrush(Colors.LightSalmon); }
}
private PropertyChangedEventHandler propertyChangedEvent;
public void SendPropertyChanged(string propertyName)
{
VerifyCalledOnUIThread();
if (propertyChangedEvent != null)
propertyChangedEvent(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged
{
add
{
VerifyCalledOnUIThread();
propertyChangedEvent += value;
}
remove
{
VerifyCalledOnUIThread();
propertyChangedEvent -= value;
}
}
[Conditional("Debug")]
protected void VerifyCalledOnUIThread()
{
Debug.Assert(Dispatcher.CurrentDispatcher == Dispatcher.CurrentDispatcher, "Call must be made on UI thread.");
}
}
It is because you have these two options enabled
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
It try to virtualize it for you. Try to make them False and see if you can see your content correctly. Keep in mind setting them to false it will make your DataGrid to load much slower.
Try to do this for each column instead of making a template at resource level. This is just an example you can adapt it to your style.
<DataGridTemplateColumn Header="Some Name" IsReadOnly="False" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<RichTextBox>
<FlowDocument IsOptimalParagraphEnabled="True" IsHyphenationEnabled="True">
<Paragraph FontFamily="Segoe UI" FontSize="14">
<Run Text="{Binding Path=First ,Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" />
<Run Text="{Binding Path=FirstText ,Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" />
<Run Text="{Binding Path=SearchedText ,Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" Background="#FFE34C"/>
<Run Text="{Binding Path=SecondText ,Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" />
</Paragraph>
</FlowDocument>
</RichTextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Seems you are using a third party grid ? issue is with how it is virtualized
i had this issue once and the reason was I was using an Identity column of the grid and hence the column was auto-generated.
the grid is being virtualized and hence cant calculate no of rows above or below the view.
you can either stop virtualization if you don't need it (200 rows or so wont make any difference)
or add a new column that is bound to index of item in the collection, that should work

Listbox inside pivot

I've got this code. I need to have access to the ScheduleList from my c# code. But it's inaccessible. I can get access to SchedulePivot only.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="0,0,0,50">
<Pivot x:Name="SchedulePivot" Margin="10,10,10,0" Title="Pivot" VerticalAlignment="Top">
<Pivot.ItemTemplate>
<DataTemplate>
<ListBox x:Name="ScheduleList" Margin="0,0,0,17" Width="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="52" Width="auto">
Searching on StackOverflow I have found this code:
private DependencyObject FindChildControl<T>(DependencyObject control, string ctrlName)
{
int childNumber = VisualTreeHelper.GetChildrenCount(control);
for (int i = 0; i < childNumber; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(control, i);
FrameworkElement fe = child as FrameworkElement;
// Not a framework element or is null
if (fe == null) return null;
if (child is T && fe.Name == ctrlName)
{
// Found the control so return
return child;
}
else
{
// Not found it - search children
DependencyObject nextLevel = FindChildControl<T>(child, ctrlName);
if (nextLevel != null)
return nextLevel;
}
}
return null;
}
I use this line to get the child:
ListBox listCont = FindChildControl<ListBox>(this, "ScheduleList") as ListBox;
Also I tried doing like this:
ListBox listCont = FindChildControl<ListBox>(SchedulePivot, "ScheduleList") as ListBox;
than I do this:
listCont.Items.Add(items);
And get the exeption as listCont=null. What's wrong I'm doing?
I have tested your code, both of the following code work well in my side and I can get the correct result:
ListBox listCont = FindChildControl<ListBox>(this, "ScheduleList") as ListBox;
ListBox listCont = FindChildControl<ListBox>(SchedulePivot, "ScheduleList") as ListBox;
If we want to access the control by using the VisualTreeHelper, we should make sure that we have not called the above code inside the constructor of the MainPage, or we will get the null result as below. Because the control does not been initialized completely:
In order to get the correct result, we need to call the above code inside the MainPage.Loaded event or Button click event to make sure that control has been initialized completely, after that it should work fine.
The following is my sample, please try to refer to:
In the MainPage.xaml:
<Pivot x:Name="SchedulePivot" ItemsSource="{Binding PivotTestlist}" Margin="10,10,10,0" Title="Pivot" VerticalAlignment="Top">
<Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding header}"></TextBlock>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate>
<ListBox x:Name="ScheduleList" Margin="0,0,0,17" Width="Auto" ItemsSource="{Binding ListBoxTestlist}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="52" Width="auto">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding id}"></TextBlock>
<TextBlock Text="{Binding name}"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</Pivot.ItemTemplate>
</Pivot>
<Button Click="Button_Click" Content="Button"></Button>
In the MainPage.xaml.cs:
public class ListBoxTest
{
public string name { get; set; }
public string id { get; set; }
}
public class PivotTest
{
public List<ListBoxTest> ListBoxTestlist { get; set; }
public string header { get; set; }
}
public sealed partial class MainPage : Page
{
public List<PivotTest> PivotTestlist { get; set; }
public MainPage()
{
this.InitializeComponent();
PivotTestlist = new List<PivotTest>();
PivotTest PivotTest1 = new PivotTest();
PivotTest1.ListBoxTestlist = new List<ListBoxTest>();
PivotTest1.ListBoxTestlist.Add(new ListBoxTest() { name = "name1", id = "id1" });
PivotTest1.ListBoxTestlist.Add(new ListBoxTest() { name = "name2", id = "id2" });
PivotTest1.header = "header1";
PivotTestlist.Add(PivotTest1);
PivotTest PivotTest2 = new PivotTest();
PivotTest2.ListBoxTestlist = new List<ListBoxTest>();
PivotTest2.ListBoxTestlist.Add(new ListBoxTest() { name = "name11", id = "id11" });
PivotTest2.ListBoxTestlist.Add(new ListBoxTest() { name = "name22", id = "id22" });
PivotTest2.header = "header2";
PivotTestlist.Add(PivotTest2);
this.DataContext = this;
}
private DependencyObject FindChildControl<T>(DependencyObject control, string ctrlName)
{
int childNumber = VisualTreeHelper.GetChildrenCount(control);
for (int i = 0; i < childNumber; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(control, i);
FrameworkElement fe = child as FrameworkElement;
// Not a framework element or is null
if (fe == null) return null;
if (child is T && fe.Name == ctrlName)
{
// Found the control so return
return child;
}
else
{
// Not found it - search children
DependencyObject nextLevel = FindChildControl<T>(child, ctrlName);
if (nextLevel != null)
return nextLevel;
}
}
return null;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ListBox listCont = FindChildControl<ListBox>(SchedulePivot, "ScheduleList") as ListBox;
int count = listCont.Items.Count;
}
}
The result:
Declare the Pivot in a storyboard and use the x:Key instead of x:Name.
e.g.
<StoryBoard>
<Pivot x:key="nameIt"/>
</StoryBoard>
private void AccesPivot ()
{ //now you can acces your pivot
}

How to use Key.Down to navigate items across ListViews

My scenario as below:
<ListView ItemsSource={Binding TestList}>
<ListView.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Key, BindingMode=OneWay}">
<Expander.Content>
<ListView ItemsSource={Binding Value}>
<ListView.ItemContainerStyle>
<Style TargetType = {ListViewItem}>
<Setter Property = "IsSelected" Value={Binding IsSelected}/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Expander.Content>
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
TestList:
Dictionary<string,ObservableCollection<Test>> TestList;
Test:
class Test : NotificationObject
{
public string Name { get; set;}
private bool isSelected;
public bool IsSelected
{
if( value != isSelected )
{
isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
}
The inner ListView SelectionMode is Single, I can use keyboard up and down buttons to navigate items in the ListView. Is there a way to implement this scenario: When the focus is on the last item of a ListView, press down key, the focus is on the first item of the next ListView; when the focus is on the first item of a ListView, press up key, the focus is on the last item of the previous ListView.
Anyone can help?
Solution
public static List<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
List<T> list = new List<T>();
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
list.Add((T)child);
}
List<T> childItems = FindVisualChildren<T>(child);
if (childItems != null && childItems.Count() > 0)
{
foreach (var item in childItems)
{
list.Add(item);
}
}
}
}
return list;
}
private void ListView_PreviewKeyDown(object sender, KeyEventArgs e)
{
ListView currentListView = sender as ListView;
List<ListView> listviews = FindVisualChildren<ListView>(ModulesListView);
int currentListViewIndex = listviews.IndexOf(currentListView);
// If the press key is Down and the selected item is the last one
if (Keyboard.IsKeyDown(Key.Down)&& currentListView.SelectedIndex + 1 == currentListView.Items.Count)
{
if (currentListViewIndex + 1 < listviews.Count)
{
// Get next ListView
var nextListView = listviews.ElementAt(currentListViewIndex + 1);
if (nextListView.Items != null && nextListView.Items.Count > 0)
{
ListViewItem item = nextListView.ItemContainerGenerator.ContainerFromIndex(0) as ListViewItem;
item.Focus();
item.IsSelected = true;
e.Handled = true;
}
}
}
else if (Keyboard.IsKeyDown(Key.Up)&& currentListView.SelectedIndex == 0)
{
if (currentListViewIndex > 0)
{
var previousListView = listviews.ElementAt(currentListViewIndex - 1);
if (previousListView.Items != null && previousListView.Items.Count > 0)
{
ListViewItem item = previousListView.ItemContainerGenerator.ContainerFromIndex(previousListView.Items.Count - 1) as ListViewItem;
item.Focus();
item.IsSelected = true;
e.Handled = true;
}
}
}
}
This problem has been solved! I have paste the solution below the question, I hope it could help other members who has the same demand, thanks!
Here you go, I tried to solve it by attached properties
using attached you can implement attachable behavior which are difficult to implement in xaml
here is what you need
xaml
<Grid xmlns:l="clr-namespace:CSharpWPF">
<ListView l:NavigationHelper.IsEnabled="True">
<sys:String>item 1</sys:String>
<sys:String>item 2</sys:String>
<sys:String>item 3</sys:String>
<sys:String>item 4</sys:String>
<ListView.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding}">
<Expander.Content>
<ListView ItemsSource="{Binding}"
l:NavigationHelper.IsEnabled="True">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected"
Value="{Binding IsSelected}" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Expander.Content>
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
I have attached a property l:NavigationHelper.IsEnabled="True" to my list views in order to enable the behavior
NavigationHelper.cs
namespace CSharpWPF
{
public class NavigationHelper : DependencyObject
{
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
// Using a DependencyProperty as the backing store for IsEnabled. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(NavigationHelper), new PropertyMetadata(false, OnIsEnabledChanged));
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Selector selector = d as Selector;
if ((bool)e.NewValue)
selector.PreviewKeyDown += selector_PreviewKeyDown;
else
selector.PreviewKeyDown -= selector_PreviewKeyDown;
}
static void selector_PreviewKeyDown(object sender, KeyEventArgs e)
{
Selector selector = sender as Selector;
if (selector.Items.Count == 0)
return;
ListViewItem itemToSelect = null;
if (e.Key == Key.Up && selector.SelectedIndex == 0)
{
itemToSelect = selector.ItemContainerGenerator.ContainerFromIndex(selector.Items.Count - 1) as ListViewItem;
}
else if (e.Key == Key.Down && selector.SelectedIndex == selector.Items.Count - 1)
{
itemToSelect = selector.ItemContainerGenerator.ContainerFromIndex(0) as ListViewItem;
}
if (itemToSelect != null)
{
selector.SelectedItem = itemToSelect;
itemToSelect.Focus();
e.Handled = true;
}
}
}
}
in this class I have created an attached property and attached PreviewKeyDown when enabled
and in the event handler I am checking if the up key is pressed and the selection is on top then choose the last item and select it and move focus
same with the bottom most item and key is down and choose the first item in this case.
and most important mark the event consumed by setting e.Handled = true so listview does not process it further.

Binding in WPF not working as expected

I have a MainWindow.xaml, MainwindowViewModel.cs, HaemogramReport.xaml and HaemogramReport.xaml.cs. I have other files as well in my project, but the problem lies in the above mentioned four files.
I am posting the minimal code here so that others can catch the problem.
Now in HaemogramReport.xaml I declare some controls like Grid, TextBox, TextBlock, Rectangle, Border, ContentControl etc.
For example HaemogramReport.xaml looks like:
<Page.DataContext>
<vm:MainWindowViewModel />
</Page.DataContext>
<Grid DataContext="{Binding Source={StaticResource Settings}}" PreviewMouseDown="Object_Selection" x:Name="Root">
<Border Style="{StaticResource BorderStyle}" x:Name="HaemogramTestBorder"
Grid.Row="{Binding Default.HaemogramTestGridRow}" Grid.Column="{Binding Default.HaemogramTestGridColumn}"
Grid.RowSpan="{Binding Default.HaemogramTestGridRowSpan}" Grid.ColumnSpan="{Binding Default.HaemogramTestGridColumnSpan}">
<Grid>
<Rectangle Fill="Transparent" x:Name="HaemogramTestRectangle"/>
<TextBlock x:Name="HaemogramTestTextBlock"
Text="{Binding Default.HaemogramTestText}" Visibility="{Binding Default.HaemogramTestVisibility}"
Background="{Binding Default.HaemogramTestBackground, Converter={StaticResource colorToSolidColorBrushConverter}}"
Foreground="{Binding Default.HaemogramTestForeground, Converter={StaticResource colorToSolidColorBrushConverter}}"
FontFamily="{Binding Default.HaemogramTestFontFamily, Converter={StaticResource stringToFontFamilyConverter}}"
FontSize="{Binding Default.HaemogramTestFontSize}"
FontWeight="{Binding Default.HaemogramTestFontWeight}" FontStyle="{Binding Default.HaemogramTestFontStyle}"
HorizontalAlignment="{Binding Default.HaemogramTestHorizontalAlignment}"
VerticalAlignment="{Binding Default.HaemogramTestVerticalAlignment}"
Margin="{Binding Default.HaemogramTestMargin}" />
</Grid>
</Border>
</Grid>
When I click on any of the element in the above declared elements, the mousedown event of the grid named Root is raised.
That event handler is in HaemogramReport.xmal.cs. Here it is:
private void Object_Selection(object sender, MouseButtonEventArgs e)
{
var mouseWasDownOn = e.Source as FrameworkElement;
if (mouseWasDownOn != null)
{
foreach (Border border in FindVisualChildren<Border>(Root))
{
border.BorderBrush = Brushes.Transparent;
}
if (!(mouseWasDownOn is Border))
{
FindParent<Border>(mouseWasDownOn).BorderBrush = Brushes.Orange;
}
MainWindowViewModel mwvm = new MainWindowViewModel();
mwvm.SelectedObj = mouseWasDownOn;
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
In mouseDown handler of Grid named Root, I say mwvm.SelectedObj = mouseWasDownOn;
SelectedObj is a property of type FrameworkElement which is declared in MainwindowViewModel.cs as follows:
private FrameworkElement selectedObj;
public FrameworkElement SelectedObj
{
get
{
return selectedObj;
}
set
{
selectedObj = value;
OnPropertyChanged("SelectedObj");
}
}
Now in my MainWindow I have for example a grid and a textBox inside it. The problematic bindings are declared here. xaml looks like:
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Grid DataContext="{Binding SelectedObj, UpdateSourceTrigger=PropertyChanged}">
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, TargetNullValue='null', FallbackValue='Error'}"/>
</Grid>
When using the above code, I always get the Text Error in above TextBox.
At the first chance I thought that this might be the binding error, so I changed my MainWindowViewModel.cs as follows:
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
SelectedObj = txt;
}
TextBlock txt = new TextBlock()
{
Text = "123"
};
private FrameworkElement selectedObj;
public FrameworkElement SelectedObj
{
get
{
return selectedObj;
}
set
{
selectedObj = value;
OnPropertyChanged("SelectedObj");
}
}
}
After making the above changes when I run my project I can see 123 in textbox but when I click on any element the text in the textbox does not change.
Now the question here is that if its a binding error then why in second example I get 123 in textbox while in 1st example I get Error - the fallback value.
And if it's not a binding error then what is the problem in above code?
Update
When I debug, I found that get part of SelectedObj is never called. But I don't know why?
Update -- Reed Copsey
Here is my new class:
public class DesignMethods
{
public static void FindCurrentlyClickedElement(DependencyObject Root, MouseButtonEventArgs e, MainWindowViewModel vm)
{
var mouseWasDownOn = e.OriginalSource as FrameworkElement;
if (mouseWasDownOn != null)
{
foreach (Border border in FindVisualChildren<Border>(Root))
{
border.BorderBrush = Brushes.Transparent;
}
if (!(mouseWasDownOn is Border))
{
FindParent<Border>(mouseWasDownOn).BorderBrush = Brushes.Orange;
}
vm.SelectedObj = mouseWasDownOn;
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
}
And I use it like:
private void Object_Selection(object sender, MouseButtonEventArgs e)
{
DesignMethods.FindCurrentlyClickedElement(Root, e, this.DataContext as MainWindowViewModel);
}
The problem is you're creating a new instance of the ViewModel, not using the existing one:
// This is not the same instance you're binding to!
// MainWindowViewModel mwvm = new MainWindowViewModel();
// Get the existing one instead
var mwvm = this.DataContext as MainWindowViewModel;
mwvm.SelectedObj = mouseWasDownOn;
Note that I would likely not use the term "ViewModel" here, though. What you are doing is very much not a typical MVVM scenario as you're tightly coupling your DataContext instance into your View, with coupling happening in both directions, which is pretty much the opposite of the normal goals of MVVM.
Edit:
You may also need to update your bindings for SelectedObj. I would recommend trying with the XAML set to:
<Grid>
<TextBox Text="{Binding SelectedObj.Text, UpdateSourceTrigger=PropertyChanged, TargetNullValue='null', FallbackValue='Error'}"/>
</Grid>
Try to use the OriginalSource instead of Source:
var mouseWasDownOn = e.OriginalSource as FrameworkElement;
because the Source property when dealing with composite controls, can be the parent that contains the OriginalSource object (in your case the grid).
I think your error might be that FrameworkElement doesn't have a Text property http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement(v=vs.110).aspx
EDIT: Try updating your binding on Text to be
{Binding SelectedObj.Text}
I think your mistake might be that you are using "common" properties instead of DependencyProperties.
As you can see on Microsoft Description
"When you define your own properties and want them to support many
aspects of Windows Presentation Foundation (WPF) functionality,
including styles, data binding, inheritance, animation, and default
values, you should implement them as a dependency property."
These are the correct types of property to fully use all resources provided by WPF
Take a look at these links
http://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/ms750428(v=vs.110).aspx
Or simply look for Dependency Property WCF on google.
Another useful link to understand the difference between these properties is
https://stackoverflow.com/a/3674727
which I used when I had similar problems.
Hope it helps!

Accessing Controls in DataTemplate in ListBox in WPF

I have listbox which have DataTemplate.
I can not access controls which placed in the datatemplate.
How can I access to this controls?
<ListBox Height="344" Name="listBoxMedicine" Width="881">
<ListBox.ItemTemplate>
<DataTemplate >
<TextBlock Name="myTextBlock">
</Datatemplate>
</ListBox.ItemTemplate>
</ListBox>
Thank you for your attention.
If you still want access your controls in codebehaind, you can do something like this:
1) Add a new helper method somewhere:
public static IEnumerable<Visual> ToVisualTree(this Visual visual)
{
yield return visual;
int numVisuals = VisualTreeHelper.GetChildrenCount(visual);
for (int i = 0; i < numVisuals; ++i)
{
var child = (Visual)VisualTreeHelper.GetChild(visual, i);
if (child == null) yield break;
foreach (var subItem in child.ToVisualTree())
{
yield return subItem;
}
}
}
2) Use it like this:
var allTextBlocks = listBoxMedicine.ToVisualTree().OfType<TextBlock>().ToList();
But I still strongly recomend to refactor your data model.
Based on the comments i would suggest you create a view-model which simply provides a property for the visbility, e.g.:
public class DataViewModel : INotifyPropertyChanged
{
private Data _data;
// Some data property.
public Data Data { get { return _data; } set { ... } }
private Visibility _visibility;
// The visibility property.
public Visibility Visibility { get { return _visibility; } set { ... } }
}
You can then bind that visibility and later set it in code to affect the view:
<DataTemplate >
<TextBlock Name="myTextBlock" Visibility="{Binding Visibility}">
</Datatemplate>
I'm using this approach to get FrameworkElement from ItemsControl, also will work with ListBox, ListView because they all inherit from ItemsControl.
private void CheckBounds(ItemsControl itemsControl)
{
foreach (var item in itemsControl.Items)
{
var child = ((FrameworkElement)itemsControl.ItemContainerGenerator.ContainerFromItem(item));
child.IsEnabled = child.IsControlVisible(itemsControl);
}
}

Categories