Binding problem in C# wpf - c#

I have a problem whit binding in wpf i have a textbox where i can do some input, then i try to bind the textinput to a custom usercontrol. This work for the usercontrol within RowDetailsTemplate but not in the CellTemplate. For each object in the CellTemplate i get this error output:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=ScaleTextBox'. BindingExpression:Path=Text; DataItem=null; target element is 'Chart' (Name=''); target property is 'MaxValue' (type 'Int32')
My code looks like this:
XAML
<ToolBarTray ToolBarTray.IsLocked="True" DockPanel.Dock="Top" Height="25">
<ToolBar Name="ButtonBar" >
<TextBox Height="23" Name="ScaleTextBox" Width="120" Text="400"/>
</ToolBar>
</ToolBarTray>
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False" IsReadOnly="True" RowHeight="25" RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" >
<my:UserControl ItemsSource="{Binding Path=Samples}" MaxValue="{Binding ElementName=ScaleTextBox, Path=Text}"/>-->
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
<DataGrid.Columns>
<DataGridTemplateColumn MinWidth="150" Header="Chart" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<my:UserControl ItemsSource="{Binding Path=Samples}" MaxValue="{Binding ElementName=ScaleTextBox, Path=Text}"/><!-- this is the problem -->
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
C#
public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(int), typeof(Chart), new FrameworkPropertyMetadata(MaxValuePropertyChanged));
private static void MaxValuePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine(e.NewValue);
}
What do i do wrong?

You can try this:
<DataTemplate>
<my:UserControl
ItemsSource="{Binding Path=Samples}"
MaxValue="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:YourControlClassName}}, ElementName=ScaleTextBox, Path=Text}"/>
</DataTemplate>

Ok, i Solved it using ElementSpy, to see how elementSpy works look here: http://joshsmithonwpf.wordpress.com/2008/07/22/enable-elementname-bindings-with-elementspy/
and the xaml:
<my:UserControl local:ElementSpy.NameScopeSource="{StaticResource ElementSpy}" ItemsSource="{Binding Path=Samples}" MaxValue="{Binding ElementName=ScaleTextBox, Path=Text}" />

From this link
The Columns collection is just a property in the Datagrid; this collection is not in the logical (or visual) tree, therefore the DataContext is not being inherited, which leads to there being nothing to bind to.
Hence it works for your RowDetailsTemplate and not for your columns I guess.

Related

Access Editing Cell of DataGrid in C# wpf

I made a Datagrid with XAML:
<DataGrid Name="dataGridBlockRow" Width="Auto" AutoGenerateColumns="False" VirtualizingStackPanel.IsVirtualizing="False"
CanUserReorderColumns="False" CanUserAddRows="false" CanUserDeleteRows="false" VerticalScrollBarVisibility="Visible"
BeginningEdit="dataGridBlockRow_BeginningEdit"
LoadingRow="gridColumns_LoadingRow" Grid.Column="1" Grid.Row="2">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Row Number" MinWidth="120" Width="1*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="0" Text="{Binding Path=RowNumber}">
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<toolkit:UIntegerUpDown Name="InputNumber" Value="{Binding Path=RowNumber, Mode=TwoWay}" AllowTextInput="False">
</toolkit:UIntegerUpDown>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Tree Count" MinWidth="120" Width="1*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="0" Text="{Binding Path=TreeCount}">
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<toolkit:UIntegerUpDown Name="inputTree" Value="{Binding Path=TreeCount, Mode=TwoWay}">
</toolkit:UIntegerUpDown>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
in c# code I added a List to my DataGrid:
List<BlockRow> blockRowList = new List<BlockRow>();
dataGridBlockRow.ItemsSource = blockRowList;
I want to Access the input control InputNumber and InputTree.
How can I access the current Editing Cell Input TextBox?
In WPF better to avoid change controls inside DataTemplate.
But if you want to get reference you can use FindName method of dataTemplate.
public static Control GetControlFromCellEditingTemplate(this DataGridColumn column, string controlName)
{
DataTemplate template = column.CellEditingTemplate;
return template.FindName(controlName, column);
}
you can use it like
var inputNumberUpDown = ((DataGridColumn)dataGridBlockRow.Columns[0]).GetControlFromCellEditingTemplate("InputNumber") as UIntegerUpDown;
var inputTreeUpDown = ((DataGridColumn)dataGridBlockRow.Columns[1]).GetControlFromCellEditingTemplate("inputTree") as UIntegerUpDown;

C# WPF - Binding to parent-ViewModel within DataGrid not working

I am trying to access a binding to my ViewModels' DependencyProperty from within a DataGrid after it is binded to an itemssource.
While from outside the DataGrid Bindings work well and Bindings within the DataGrid work for elements of the Itemssource well too, I am not able to get a binding to my viewModel within the DataGrid.
I tried several aproaches.
I want to use that non-working binding to achieve:
- load the column-header via a dependency Property from the viewModel
- load a bool Value from the ViewModel which determines whether a column is read-only
This is one of my DependencyProperties I have specified in the ViewModel:
public static readonly DependencyProperty FirstColDataGridLabelProperty = DependencyProperty.Register(
"FirstColDataGridLabel", typeof(string), typeof(MyVm), new PropertyMetadata(default(string)));
In the constructor of the viewlModel some values are assigned to the dependencyProperties.
Edit: Added the bounding grid and UserControl-Tag to the Data-Grid:
MyList is a List, containing the data (FirstCol, SecondCol, etc.)
<UserControl x:Class="Aidb.GeoMonPlus.Wpf.Control.EditSpatialReferenceListControl"
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:viewModel="clr-namespace:Aidb.GeoMonPlus.ViewModel.Control;assembly=Aidb.GeoMonPlus.ViewModel"
Width="Auto"
Height="Auto"
MinWidth="400"
MinHeight="200"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="1000"
x:Name="myControl">
<Grid>
<DataGrid Grid.Column="0" Grid.Row="0"
ItemsSource="{Binding MyList}"
CanUserResizeColumns="True"
CanUserResizeRows="True"
CanUserSortColumns="True"
CanUserAddRows="False"
AlternatingRowBackground="Gainsboro"
AlternationCount="2"
AutoGenerateColumns="False"
BorderBrush="Black"
GridLinesVisibility="Vertical">
<DataGrid.Columns>
<DataGridTextColumn Header="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type viewModel:MyVm}}, Path=FirstColDataGridLabel}" Binding="{Binding FirstCol}" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Header="{Binding SecondColDataGridLabel, FallbackValue='AnzeigeraumbezugDataGridLabel'}" Binding="{Binding (viewModel:EditSpatialReferenceListVm.GCSDataGridLabel) }" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Header="{Binding (viewModel:MyVm.ThirdColDataGridLabel)}" Binding="{Binding ThirdCol}" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Header="{Binding ElementName=myControl, Path=DataContext.FourthColDataGridLabel}" Binding="{Binding FourthCol}" Width="Auto" IsReadOnly="True">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Margin" Value="0,0,-1,0"></Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Column="0" Grid.Row="1"
Margin="0,20,0,20"
Width="75"
Height="Auto"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding BtnShowSubmit}"
Content="{Binding BtnShowLabel, FallbackValue='BtnShowLabel'}" />
None of the four tries for a dataBinding for the header-names are working. All headers of the table are empty or the fallback-value is shown.
The binding of the itemssource works, data is shown as expected in the datagrid.
The binding in the button to show the buttons text works as expected.
Is this just not possible for DataGrid or how do I access ViewModels' DependecyProperties from inside the dataGrid after it was binded to an itemssource?
Edit2:
As it was asked, this is my ViewModels Base Class:
public class ViewModelBase : DependencyObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected static void PropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var vmBase = sender as ViewModelBase;
vmBase?.OnPropertyChanged(args.Property.Name);
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Edit3:
I found some errormessages:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='MyProject.ViewModel.Control.EditSpatialReferenceListVm',
AncestorLevel='1''. BindingExpression:Path=GCSDataGridLabel;
DataItem=null; target element is 'DataGridTextColumn'
(HashCode=8060118); target property is 'Header' (type 'Object')
or
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement
or FrameworkContentElement for target element.
BindingExpression:Path=(viewModel:EditSpatialReferenceListVm.GCSDataGridLabel);
DataItem=null; target element is 'DataGridTextColumn'
(HashCode=16639474); target property is 'Header' (type 'Object')
Searching for this leads to that post:
WPF Error: Cannot find governing FrameworkElement for target element
Where it is stated out from user 'WPF-it' that:
Sadly any DataGridColumn hosted under DataGrid.Columns is not part of
Visual tree and therefore not connected to the data context of the
datagrid.
He suggest to use a proxy-control.
I just went through the same thing. There are several methods posted for getting around this and I can see you have tried some, but none of them worked for me either except one.
Create a control just above your datagrid and give it a name:
<TextBlock x:Name="tempcontrol" Text="{Binding HeaderText}" Visibility="Collapsed"/>
and then bind this temp control to the data you want and set it's visibility to collapsed.
Then bind your DataGridColumn to the temp controls property:
<DataGridTextColumn Header="{Binding Path=Text, Source={x:Reference tempControl}}"/>
My case was little different because I was binding to the Visibilty property so I used a FrameworkElement instead of a TextBlock for the dummy control, but I think this should work as well.
As mentioned in the mainpost (edit 3), it indeed is not working with datagrid.
But using DataView can handle that.
Using a ListView with an inner GridView instead of a DataGrid also is possible to solve that.
With that the following bindings do work:
<GridViewColumn Header="{Binding HoehenbezugsrahmenDataGridLabel, FallbackValue='HoehenbezugsrahmenDataGridLabel'}" DisplayMemberBinding="{Binding Hoehenbezugsrahmen}" Width="250" />
<GridViewColumn Header="{Binding ElementName=myControl, Path=DataContext.TransformationDataGridLabel, FallbackValue='AnzPunkteGeoMonDataGridLabel'}" DisplayMemberBinding="{Binding Transformation}" Width="250" />
<GridViewColumn Header="{Binding Path=Text, Source={x:Reference tmpHeader}}" DisplayMemberBinding="{Binding AnzPunkteGeoMon}" Width="250" />
<GridViewColumn DisplayMemberBinding="{Binding Ursprungsraumbezug}" Width="250">
<GridViewColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding ElementName=myControl, Path=DataContext.TransformationDataGridLabel, FallbackValue='AnzPunkteGeoMonDataGridLabel'}"/>
</DataTemplate>
</GridViewColumn.HeaderTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Ursprungsraumbezug}" Width="250">
<GridViewColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Text, Source={x:Reference tmpHeader}}"/>
</DataTemplate>
</GridViewColumn.HeaderTemplate>
</GridViewColumn>
All other mentioned bindings do not work, headers keep being empty.
Again using Ancestor does not work.
Especially the first approach is the shortest one.
Due to this I am going to change from DataGrid to ListView.

Set Binding of DataGridTextColumn's TextBlock

I'm trying to change the binding for the "Text" property for a TextBox inside of a DataGrid. Consider the following code:
<DataGrid Name="CoreView837" HorizontalAlignment="Left" Height="366" VerticalAlignment="Center" Width="792"
AutoGenerateColumns="False" SelectionUnit="Cell" SelectionMode="Extended" VerticalScrollBarVisibility="Auto" ItemsSource="{Binding}">
<DataGrid.Columns>
<DataGridTextColumn x:Name="ClaimNumber" Header="Claim Number" Width="350" Binding="{Binding ClaimNumber}">
</DataGridTextColumn>
<DataGridTemplateColumn x:Name ="Parent837" Header="837 Segment" Width="300" ClipboardContentBinding="{Binding SegmentText}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ScrollViewer MaxHeight="100">
<TextBlock x:Name="Segment837" Text="{Binding SegmentText}" TextWrapping="Wrap"/>
</ScrollViewer>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Errors" Width="350" ClipboardContentBinding="{Binding ErrorText}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ScrollViewer MaxHeight="100">
<TextBlock Text="{Binding ErrorText}" TextWrapping="Wrap"/>
</ScrollViewer>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I want to change the binding of the TextBlock "Segment837" in the code behind to a new binding, like below:
Segment837.Binding = new Binding("Value");
However, it seems that I can't change the Text binding of the TextBlock directly. It seems I can only select "Parent837."
Can anyone suggest to me how to change the binding either directly or even a better way? Right now I have the binding set for when a text file's broken segments are successfully loaded, but I wanted to change the binding of the TextBlock for instance in the case that it fails and I want to return alternative data.

DataGridTemplateColumn Header Binding

I have a dynamically created DataGrid with the following XAML:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Cells}" IsReadOnly="True" Visibility="{Binding VisibilityStatus}"
CanUserResizeColumns="False" CanUserAddRows="False" RowBackground="{Binding Color}" Margin="0" RowHeight="25" HeadersVisibility="All">
<DataGrid.Columns>
<DataGridTemplateColumn Header="{Binding Path=Header}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding UniqueValuesToLapMapping.Keys, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedValue, Mode=TwoWay}"
SelectedIndex="0" Width="55"
Background="{Binding Color}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
The table is being displayed correctly with all of its content except for the header. No matter in which class I put the Header Property I can't seem to bind to it. I've tried putting it into the ViewModel, putting it into the class that has the "Cells" property, and putting it into the class that has all the Properties that I bind to in the ComboBox.
I have also tried binding without the "Path=" and setting the UpdateSourceTrigger to OnPropertyChanged and changing the properties value on the runtime: nothing seems to work. What am I missing here?
This is the Property I am binding to that I've put in all these classes:
private string _header = "TEST";
public string Header
{
get { return _header; }
set { SetField(ref _header, value, "Header"); }
}
It turns out that the was not in any logical or visual tree. This solution worked for me:
http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/

How to bind to the ItemsSource of a DataDrid from inside DataRow?

I'm working with a window with several DataGrid's, and I'd like to process deletion event via a single command.
For this, I need to pass to that command the list of records, from which the record has to be deleted.
Here's what I mean:
<DataGrid Margin="0" HeadersVisibility="None"
ItemsSource="{Binding GroupExtednedDataList}"
... >
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="delete"
CommandParameter="{Binding:::
How do I bind from here to GroupExtednedDataList from ItemsSoruce?}" >
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
How do I bind from inside <Button Content="delete" to ItemsSource of the DataGrid?
Something like
{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=DataGrid}}

Categories