Unable to bind viewmodel property - my own user control (WPF) [duplicate] - c#

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
Closed 2 years ago.
The problem is quite known and popular ...
I went through many threads today, but none of them allowed me to solve my problem, so I decided to ask you for help. I think I follow all the tips of other users...
CustomControl.xaml:
<UserControl x:Class="X.UserControls.CustomUserControl"
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"
mc:Ignorable="d"
x:Name="eqAction"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{Binding Path=FirstName, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</UserControl>
CustomControl.xaml.cs:
public partial class CustomControl : UserControl
{
public static readonly DependencyProperty FirstNameProperty = DependencyProperty.Register(
"FirstName",
typeof(string),
typeof(CustomControl),
new FrameworkPropertyMetadata(default(string),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string FirstName
{
get
{
return (string)GetValue(FirstNameProperty);
}
set
{
SetValue(FirstNameProperty, value);
}
}
public CustomControl()
{
InitializeComponent();
}
}
MainViewModel.cs:
private string _test;
public string Test
{
get { return _test; }
set
{
_test = value;
OnPropertyChanged(string.Empty);
}
}
public MainViewModel()
{
Test = "abc";
}
public new event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
MainUserControl.xaml
<StackPanel x:Name="NotWorking">
<userControls:CustomControl FirstName="{Binding Path=Test, Mode=TwoWay}" />
</StackPanel>
<StackPanel x:Name="Working">
<userControls:CustomControl FirstName="My string value that works" />
</StackPanel>
<StackPanel x:Name="WorkingToo">
<TextBlock Text="{Binding Path=Test, Mode=TwoWay}" />
</StackPanel>
In my MainUserControl.xaml file I have three stackpanels. The first is what I want to achieve. Unfortunately, no data is currently binded (nothing displayed).
However, when instead of binding I assign a string value to the property, the text will be displayed correctly (example 2).
Example 3: when I create a binding to e.g. a textblock component, the text will also be displayed ... Only one control that appears in the first stackpanel (name NotWorking) works differently ...
Do you see the mistake?

the problem is the line where you set the datacontext in the MainUserControl:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
When you set the DataContext as in your code, the binding in the MainUserControl will look for a property named Test in the CustomControl and not in the MainViewModel.

Related

bind a textbox to a property

All I want is when a user changes the value in the textbox alphaMin_txt, the property AlphaMin gets updated.
Code behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _alphaMin;
public string AlphaMin
{
get { return _alphaMin; }
set
{
if (value != _alphaMin)
{
_alphaMin = value;
NotifyPropertyChanged();
}
}
}
}
XAML:
<DockPanel DataContext="{Binding MainWindow}">
<TextBox Text="{Binding
Path=AlphaMin,
NotifyOnTargetUpdated=True,
Mode=OneWayToSource,
UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
This should be a duplicate a hundred times over but I've been through it all and none of it is laid out plain and simple for this one-way update of the source. All the MSN tutorials are binding some UIControl to another, which is pointless because IntelliSense shows you how to do that.
Your DockPanel probably has a faulty DataContext binding. DataContext should be set at the window level.
<Window ... DataContext="{Binding RelativeSource={RelativeSource Self}}" ..>
Of course, this is assuming your XAML is MainWindow.xaml.
If you have a different DataContext for the rest of the MainWindow, then you can do this:
<TextBox Text="{Binding
RelativeSource={RelativeSource AncestorType=Window},
Path=AlphaMin,
NotifyOnTargetUpdated=True,
Mode=OneWayToSource,
UpdateSourceTrigger=PropertyChanged}" />
Of course, you should remove the DataContext for the DockPanel.
Your code behind is correct; there are no changes needed. Using CallerMemberName is a good way to implement INotifyPropertyChanged.
Assign a name to <Window x:Name="MyWin"...> , then change DataContext binding to {Binding ElementName=MyWin}.
Change this NotifyPropertyChanged(); to NotifyPropertyChanged("AlphaMin");

How to make a datagrid see edits in a control?

I have a user control that I am using to populate a datagrid.
I would like the user to be able to add items by editing the empty row at the bottom. (This is why I am using a datagrid rather than an itemscontrol) However the datagrid does not realise that the last item is edited unless the user clicks the background of the control. I would like the new item to be added when the user makes changes on the properties that the control exposes.
XAML of the control:
<UserControl x:Class="ControlTest.MyControl"
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:ControlTest"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="300"
DataContext="{Binding Path=., Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
>
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding Path=p1, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Width="300"
Height="30"
VerticalAlignment="Center"/>
<ComboBox ItemsSource="{Binding Path=DropDownValues,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=local:MyControl}}"
SelectedItem="{Binding Path=p2, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Height="30"/>
</StackPanel>
</UserControl>
cs:
public partial class MyControl : UserControl
{
private static readonly DependencyProperty DropDownValuesProperty =
DependencyProperty.Register(
"DropDownValues",
typeof(List<String>),
typeof(MyControl),
new FrameworkPropertyMetadata(new List<String>()
));
public List<String> DropDownValues
{
get
{
return (List<String>)GetValue(DropDownValuesProperty);
}
set
{
SetValue(DropDownValuesProperty, value);
}
}
public MyControl()
{
InitializeComponent();
}
}
DataGrid XAML
<DataGrid
AutoGenerateColumns="False"
ItemsSource="{Binding objs, Mode=TwoWay}"
HeadersVisibility="None"
Margin="0,0,0.4,0"
CanUserAddRows="True"
>
<DataGrid.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</DataGrid.ItemsPanel>
<DataGrid.Columns>
<DataGridTemplateColumn Width="300">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="local:Measure">
<local:MyControl
DataContext="{Binding ., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DropDownValues=
"{Binding DataContext.list, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
Width="300"
/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Can I make this work, and/or is there a better way to do this?
I would like to suggest you a different way of doing that:
set CanUserAddRows=false on your DataGrid and then manually add rows to the ObservableCollection<Something> to which your DataGrid is bound to.
OR
If you are still interested in the approach that you follow:
In your xaml file:
<DataGrid x:Name="myDataGrid" CellEditEnding="DataGrid_CellEditEnding" .....>
<!--Some Code-->
</DataGrid>
Then in the Code-Behind:
private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
myDataGrid.CommitEdit();
}
If you don't understand anything then feel free to ask.
Update
If you are following the same approach:
In your DataGrid's Beginning edit event you can try:
private void DataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
if ((selectedRow as DataGridRow).Item.ToString() != "{NewItemPlaceholder}")
{
//Here you can add the code to add new item. I don't know how but you should figure out a way
}
}
Note: The code mentioned above is not tested.
I would also suggest you :
Not to use DataGrid. Instead use ListBox. Because, you are trying to add some data. At this time you never need sorting, searching and column-reordering fascilities. In such scenario, ListBox is useful as it is light-weight control than datagrid. I have a sample here: https://drive.google.com/open?id=0B5WyqSALui0bTXFGZWxQUWVRdkU
Is the problem that the UI is not being notified of changes to the objs collection? What I would do is try setting up whatever view model that contains objs to make objs an observable collection. I would also ensure that whatever objs is an observable collection of implements INotifyPropertyChanged and that properties p1 and p2 both fire OnPorpertyChanged when they are set.
public ObservableCollection<YourObject> objs
and
public class YourObject : INotifyPropertyChanged
{
protected void OnPropertyChanged(string Name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(Name));
}
}
private string _p1;
public string p1
{
get { return _p1; }
set
{
if (_p1 != value)
{
_p1 = value;
OnPropertyChanged("p1");
}
}
}
private string _p2;
public string p2
{
get { return _p2; }
set
{
if (_p2 != value)
{
_p2 = value;
OnPropertyChanged("p2");
}
}
}
}

WPF Binding Cannot find source for binding with reference

I've been following this answer to expose some properties of my user-control.
The problem being that the binding doesn't find the source and I don't understand how to do it properly.
XAML:
<UserControl x:Class="Project.UI.Views.ucFilterDataGrid"
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:Project.UI.Views"
xmlns:watermark="clr-namespace:Project.UI.Watermark"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="0,0,00,30"/>
</Style>
</StackPanel.Resources>
<AdornerDecorator>
<TextBox Name="SearchTextBox">
<watermark:WatermarkService.Watermark>
<TextBlock Name="waterMarkText"
Text="{Binding Path=WatermarkContent,
RelativeSource={RelativeSource FindAncestor,
AncestorType=local:ucFilterDataGrid}}"
HorizontalAlignment="Center" >
</TextBlock>
</watermark:WatermarkService.Watermark>
</TextBox>
</AdornerDecorator>
<DataGrid Name="Results">
</DataGrid>
</StackPanel>
CS:
namespace Project.UI.Views
{
/// <summary>
/// Interaction logic for ucFilterDataGrid.xaml
/// </summary>
public partial class ucFilterDataGrid : UserControl
{
public ucFilterDataGrid()
{
InitializeComponent();
}
public string WatermarkContent
{
get { return GetValue(WatermarkContentProperty).ToString(); }
set { SetValue(WatermarkContentProperty, value); }
}
public static readonly DependencyProperty WatermarkContentProperty = DependencyProperty.Register("WatermarkContent", typeof(string), typeof(ucFilterDataGrid), new FrameworkPropertyMetadata(string.Empty));
}
}
Window:
<Grid>
<local:ucFilterDataGrid Margin="301,34,31,287" WatermarkContent="MyTest"/>
</Grid>
The result will be a blank TextBlock. If I just remove it from my watermark UserControl and put it on the same level as the DataGrid, it will work is intended.
The problem here is your TextBlock is set as a value of an attached property, here it is:
<watermark:WatermarkService.Watermark>
<TextBlock ...>
</TextBlock>
</watermark:WatermarkService.Watermark>
watermark:WatermarkService.Watermark is an attached property. Its value is just an object in memory and detached from the visual tree. So you cannot use Binding with RelativeSource or ElementName. You need some proxy to bridge the disconnection. The Source will be used for Binding, the code you should try is as follow:
<TextBox Name="SearchTextBox">
<TextBox.Resources>
<DiscreteObjectKeyFrame x:Key="proxy"
Value="{Binding Path=WatermarkContent,
RelativeSource={RelativeSource FindAncestor,
AncestorType=local:ucFilterDataGrid}}"/>
</TextBox.Resources>
<watermark:WatermarkService.Watermark>
<TextBlock Name="waterMarkText"
Text="{Binding Value, Source={StaticResource proxy}}"
HorizontalAlignment="Center" >
</TextBlock>
</watermark:WatermarkService.Watermark>
</TextBox>
I made something similar the other day, and if I remember correctly. You will have to derive from the INotifyPropertyChanged interface and tell the component that the property has changed whenever you update the WatermarkContent. Otherwise the xaml (view) will not know when you change the Text, and the binding wont update.
Here is what you can try out
using System.ComponentModel;
public partial class ucFilterDataGrid : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty WatermarkContentProperty = DependencyProperty.Register("WatermarkContent", typeof(string), typeof(ucFilterDataGrid), new FrameworkPropertyMetadata(string.Empty));
public event PropertyChangedEventHandler PropertyChanged;
public ucFilterDataGrid()
{
InitializeComponent();
}
public string WatermarkContent
{
get { GetValue(WatermarkContentProperty).ToString(); }
set {
SetValue(WatermarkContentProperty, value);
RaisePropertyChanged();
}
}
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I've added the INotifyPropertyChanged and raises the event each time the WatermarkContent is changed.
Hope it helped!

Custom usercontrol property binding failure silverlight

I have a custom usercontrol with DataContext="{Binding RelativeSource={RelativeSource self}}"
On the code behind i've made a dependency property like:
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) => { new Base().OnPropertyChanged("ElementName"); })));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
Now when I try to use this usercontrol in my mainpage.xaml and use the following binding: <test.TestControl ElementName="{Binding name}" />, it keeps searching for 'name' property in my custom usercontrol instead of where it should come from?
What am I doing wrong ?
It searches there because you have the DataContext set on the topmost level for your user control. What you would need to do is get rid of the relative binding to self in the user control and specify ElementName in bindings (inside user control). Btw you probably don't need OnPropertyChanged in the PropertyChangedCallback cause DependencyProperties in their nature notify about value changes.
I eventually solved it this way. Not the way I wanted, but it's a (in my eyes) pretty neat solution.
CustomUserControl.xaml
<UserControl x:Class="TestApp.Controls.CustomUserControl"
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"
Width="75"
Height="75">
<Canvas x:Name="LayoutRoot"
Background="Black">
<StackPanel Orientation="Vertical">
<Image x:Name="UCImage"
Width="50"
Height="50"
HorizontalAlignment="Center" />
<TextBlock x:Name="UCText"
HorizontalAlignment="Center" />
</StackPanel>
</Canvas>
</UserControl>
CustomUserControl.xaml.cs
public partial class ElementControl : UserControl
{
#region DependencyProperty ElementNameProperty
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) =>
{
//See Here
((ElementControl)s).UCText.Text = e.NewValue as string;
})));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
#endregion
}

Binding a property in an ItemsControl ItemTemplate to a DP on the UserControl is not working

A UserControl has 3 Dependency Properties: FormatAvailabilities, Orientation and FullText. FormatAvailabilities is bound to the ItemsSource property of an ItemsControl. Orientation is bound to the Orientation property if the StackPanel which is in the ItemsPanelTemplate within the ItemsControl. FullText is bound to the Visibility property of two TextBlocks inside the DataTemplate of the ItemsControl. I am using two converters to determine which TextBlock to show: a BoolToVisibilityConverter and a BoolToInvertedVisibilityConverter (the latter is an inversion of the former). I copied the Visibility property as-is from the TextBlock (both of them, independently) to the ItemsControl and it works correctly..
It seems that the bindings on the TextBlocks are not working properly because both are always visible. Since they are both binding on the same property but one is inverted, there should never be a possibility for both to be visible at the same time.
I put a breakpoint in my converter and it is never hit, so my guess is that there is an issue with binding from within a repeating control to the outer control in which it is housed.
App.xaml:
<common:BaseApp x:Class="xyz.App" xmlns:converters="clr-namespace:xyz.Converters;assembly=xyz">
<common:BaseApp.RootVisual>
<phone:PhoneApplicationFrame x:Name="RootFrame" Source="/Home.xaml"/>
</common:BaseApp.RootVisual>
<common:BaseApp.Resources>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<converters:BoolToVisibilityConverter x:Key="BoolToInvertedVisibilityConverter" IfTrue="Collapsed" IfFalse="Visible"/>
</common:BaseApp.Resources>
</common:BaseApp>
UserControl XAML:
<UserControl
x:Name="FormatsControl"
x:Class="xyz.Formats"
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"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<ItemsControl Background="Transparent" ItemsSource="{Binding ElementName=FormatsControl, Path=FormatAvailabilities}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="{Binding ElementName=FormatsControl, Path=Orientation}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding BindsDirectlyToSource=True}" Margin="0,0,10,0" Visibility="{Binding ElementName=FormatsControl, Path=FullText, Converter={StaticResource BoolToVisibilityConverter}}"/>
<TextBlock Text="{Binding Description}" Margin="0,0,10,0" Visibility="{Binding ElementName=FormatsControl, Path=FullText, Converter={StaticResource BoolToInvertedVisibilityConverter}}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
UserControl CS:
namespace xyz
{
public partial class Formats : UserControl
{
public static readonly DependencyProperty FormatAvailabilitiesDependencyProperty = DependencyProperty.Register("FormatAvailabilities", typeof(FormatAvailability[]), typeof(Formats), null);
public static readonly DependencyProperty OrientationDependencyProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Formats), new PropertyMetadata(Orientation.Horizontal));
public static readonly DependencyProperty FullTextDependencyProperty = DependencyProperty.Register("FullText", typeof(bool), typeof(Formats), null);
public FormatAvailability[] FormatAvailabilities
{
get { return (FormatAvailability[])base.GetValue(Formats.FormatAvailabilitiesDependencyProperty); }
set { base.SetValue(Formats.FormatAvailabilitiesDependencyProperty, value); }
}
public Orientation Orientation
{
get { return (Orientation)base.GetValue(Formats.OrientationDependencyProperty); }
set { base.SetValue(Formats.OrientationDependencyProperty, value); }
}
public bool FullText
{
get { return (bool)base.GetValue(Formats.FullTextDependencyProperty); }
set { base.SetValue(Formats.FullTextDependencyProperty, value); }
}
public Formats()
{
InitializeComponent();
}
}
}
I must be over looking something...thanks!
There is an issue with naming UserControls in Silverlight 3 as described by this blog post, which is also present in the Windows Phone 7 version of Silverlight. Effectively, if you give the UserControl a name in the XAML where it is used (i.e. it's parent), then that overrides the name given in the UserControl's own XAML file.
I ran into a similar problem, instead of binding to the elementname I changed the binding to this
Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}
And that works just fine.
Looks like you are missing the OnPropertyChanged handler.
Here is one of my dependency properties. Note the changed handler.
public ObservableCollection<ObjWithDesc> ItemsSource
{
get
{
return (ObservableCollection<ObjWithDesc>)GetValue(ItemsSourceProperty);
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(ObservableCollection<ObjWithDesc>),
typeof(HorizontalListBox),
new PropertyMetadata(OnItemsSourcePropertyChanged)
);
static void OnItemsSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((HorizontalListBox) obj).OnItemsSourcePropertyChanged(e);
}
private void OnItemsSourcePropertyChanged(DependencyPropertyChangedEventArgs e)
{
ObservableCollection<ObjWithDesc> objWithDescList = (ObservableCollection<ObjWithDesc>)e.NewValue;
MainListBox.ItemsSource = objWithDescList;
}

Categories

Resources