Binding String Property in Code-Behind TextBlock - wpf

i am trying to binding a very simple property to a TextBlock, but I have to do all in code-behind (C#).
What i am trying to do is:
public string SomeText { get; set; }
And after I try the Binding on TextBlock:
Binding myBinding = new Binding(SomeText);
myTextBlock.SetBinding(TextBlock.TextProperty, myBinding);
How do I keep the Text property of the TextBlock the same of the Property SomeText.

Use BindingOperations
Binding binding = new Binding();
binding.Path = new PropertyPath("SomeText");
binding.Source = sourceObject; // view model?
BindingOperations.SetBinding(theTextBlock, TextBlock.TextProperty, binding);

Related

PropertyChanged event always null even after setting DataContext

I have a Model with INotifyPropertyChanged handling copied from tutorials:
public event PropertyChangedEventHandler? PropertyChanged;
protected void Notify(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
When I update a member of the class, I call the handler:
public string? Id
{
get => _id;
set
{
if (value != _id)
{
_id = value;
Notify(nameof(Id));
}
}
}
And in the view code behind I have:
private Goal _goal;
public GoalControl()
{
InitializeComponent();
this._goal = new MyGoal();
this.DataContext = _goal;
Binding binding = new Binding("Text");
binding.Source = _goal.Id;
binding.Mode = BindingMode.TwoWay;
_ = Id.SetBinding(TextBox.TextProperty, binding);
}
But the view doesn't pick up any changes to the field. When I debug, I find that PropertyChanged is always null. How should I set it to a useful value?
This is in a user control, by the way, which will be generated dynamically so I don't think I can do the binding from XAML.
Assuming that Id is a TextBox in your GoalControl, you would bind its Text property to the Id of the MyGoal object in the DataContext like shown below.
You do not set the Source property of the Binding, because the source object should be provided by the current DataContext. Also note that TwoWay is the default binding mode for the TextBox.Text property and does not need to be set explicitly.
public GoalControl()
{
InitializeComponent();
_goal = new MyGoal();
DataContext = _goal;
Binding binding = new Binding("Id");
Id.SetBinding(TextBox.TextProperty, binding);
}
The Binding could as well be written in XAML:
<TextBox Text="{Binding Id}"/>
Since this is in a UserControl, you should however not set the DataContext at all. UserControls, as any other controls, should not have "private" view models like your MyGoal object.
The UserControl would instead expose a dependency property Id, which is bound when you use the control, like
<local:GoalControl Id="{Binding SomeViewModelId}"/>
In the UserControl's XAML, the Binding to the own property would specify the Source object as RelativeSource:
<TextBox Text="{Binding Id,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>

WPF : binding in UserControl and Page

I cannot properly bind to a UserControl property placed in a Page.
I have this UserControl :
<UserControl x:Class="xxxx.NumericBox" (...)>
<TextBox Name="TextBoxValue" Text="{Binding RelativeSource {RelativeSource AncestorType=UserControl}, Path=Value, Mode=TwoWay}" (...)
With this behind code :
public partial class NumericBox : UserControl
{
public NumericBox()
{
InitializeComponent();
}
public uint? Value
{
get => (uint?)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(uint?), typeof(NumericBox), new PropertyMetadata(null));
The UserControl contains others controls witch interract with Value property (+/-) and it works fine.
But I create the DependencyProperty to also bind the value in parent page.
A exemple of code in a page where I inject the UserControl :
var binding = new Binding("Line.Quantity");
binding.Mode = BindingMode.TwoWay;
var numeric = new NumericBox();
numeric.SetBinding(ValueProperty, binding);
The binding works on startup but not update Line.Quantity when I modify the Textbox...
The Line class implements INotifyPropertyChanged and notify change on Quantity.
What is the correct way to do that ?
I have seen this question but but I have not been able to correct my code :
Binding on DependencyProperty of custom User Control not updating on change

WPF Programmatically Binding to a Data Grids Column Header (text) during the dataGrid_AutoGeneratingColumn event

I am trying to set the binding for a Data Grid Column Headers text during the Auto generating Column event but with no luck. headerDetails is a dictionary containing columnSettings objects that implement the INotifyPropertyChanged interface (Header setter raises an OnPropertyChanged event)
private void dataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
e.Column.Header = this.headerDetails[headername].Header;
//// rather than set the value here, create a binding
}
I have tried looking at these examples mentioned and came up with this:
Binding myBinding = new Binding();
myBinding.Source = this.headerDetails[headername].Header;
myBinding.Path = new PropertyPath("Header");
myBinding.Mode = BindingMode.TwoWay;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(e.Column, TextBox.TextProperty, myBinding);
which unfortunately doesn't work :(
MM8 answer has fixed the problem thanks, I was Binding to the variable rather than the object. the solution with notes:
Binding myBinding = new Binding();
myBinding.Source = this.headerDetails[headername]; // Source = object
myBinding.Path = new PropertyPath("Header"); // Path = Getter/Setter
myBinding.Mode = BindingMode.TwoWay;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(e.Column, DataGridTextColumn.HeaderProperty, myBinding);
You should set the Source property of the Binding to the object that implements the INotifyPropertyChanged interface:
Binding myBinding = new Binding();
myBinding.Source = this.headerDetails[headername];
myBinding.Path = new PropertyPath("Header");
myBinding.Mode = BindingMode.TwoWay;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(e.Column, DataGridTextColumn.HeaderProperty, myBinding);
This should work provided that headerDetails[headername] returns an INotifyPropertyChanged and that you then set the Header property of this very same instance.

Get Binding Source object from binding using ElementName

I need to bind something to a child of an element in my VisualTree .
in a UserControl:
<StackPanel>
<DataGrid x:Name="dataGrid" />
<Control Tag="{Binding ElementName=dataGrid}" />
</StackPanel>
in DataGrid's Template :
<Template TargetType=DataGrid>
......
<Control x:Name="FindMe" />
......
</Template>
What i thought of doing is traversing the VisualTree of the DataGrid , for this purpose iv'e created a
custom markup extension :
public class TemplatePartBinding : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
Binding binding = new Binding();
binding.ElementName = ElementName;
// HOW DO I GET THE SOURCE OBJECT FROM THE BINDING ?
DataGrid dataGrid = // Extract the DataGrid from the binding.
Control _findMe = VisualTreeHelperExtentions.FindVisualChild<Control>(dataGrid,"FindMe");
binding.Target = _findMe;
binding.Path = new PropertyPath("Tag");
return binding;
}
[ConstructorArgument("ElementName")]
public string ElementName
{
get;
set;
}
[ConstructorArgument("TemplatePartName")]
public string TemplatePartName
{
get;
set;
}
}
Here in ProvideValue i wan't to locate the DataGrid (Source Object for the binding ) after giving the binding's ElementName value it's name ,
How do i extract the DependencyObject (My DataGrid) from the binding iv'e just created ?
You can get the DataGrid instance in markup extension provide value method but FindMe Control you won't be able to get with VisualTree extension methods because when this method gets called, Visual Tree for dataGrid is not created at that time.
Morever, logical tree won't be of any help either since Control is Visual child and not logical child of dataGrid.
However, for your question to find dataGrid, you can get like this:
public override object ProvideValue(IServiceProvider serviceProvider)
{
IRootObjectProvider provider = (IRootObjectProvider)serviceProvider
.GetService(typeof(IRootObjectProvider));
DataGrid dataGrid =
LogicalTreeHelper.FindLogicalNode((DependencyObject)provider.RootObject,
ElementName) as DataGrid;
....
}
IRootObjectProvider will get you RootObject which will be UserControl and eventually can get you DataGrid by traversing LogicalTree and not VisualTree since it will return null. (Visual Tree not created yet).

WPF ObservableCollection in xaml

I have created an ObservableCollection in the code behind of a user control. It is created when the window loads:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Entities db = new Entities();
ObservableCollection<Image> _imageCollection =
new ObservableCollection<Image>();
IEnumerable<library> libraryQuery =
from c in db.ElectricalLibraries
select c;
foreach (ElectricalLibrary c in libraryQuery)
{
Image finalImage = new Image();
finalImage.Width = 80;
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri(c.url);
logo.EndInit();
finalImage.Source = logo;
_imageCollection.Add(finalImage);
}
}
I need to get the ObservableCollection of images which are created based on the url saved in a database. But I need a ListView or other ItemsControl to bind to it in XAML file like this:
But I can't figure it out how to pass the ObservableCollection to the ItemsSource of that control. I tried to create a class and then create an instance of a class in xaml file but it did not work. Should I create a static resource somehow>
Any help will be greatly appreciated.
Firstly, the ObservableCollection is a local variable. What you need to do is have it as a private global variable and expose it with a public property. You can use the INotifyPropertyChanged interface to have the image data update automagically when the actual collection itself changes.
In your XAML, you then need to set the DataContext to self, and you can then directly bind your public property to the ItemsSource. You may want to use an ItemTemplate for displaying the items in a custom manner.
Cheers,
Adam
Example as requested:
In C#:
public MyWindowClass
{
public ObservableCollection<image> MyImageCollection
{
get;
set;
}
}
In XAML:
<UserControl
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
...
<ListBox ItemsSource="{Binding MyImageCollection}" ItemTemplate="*yourtemplateresource*" />
...
</UserControl>
Now, the reason that I mentioned using INotifyPropertyChanged is that if you try:
MyImageCollection = new ObservableCollection<image>();
The items in the listbox will not automatically update. With an ObservableCollection, however, you do not need to implement INotifyPropertyChanged for basic addition and removal of list items.
You have to set the DataContext of the UserControl to your collection:
DataContext = _imageCollection
You can do that in the UserControl_Loaded() method.
Next you need to bind the ItemsSource of the ListView in the XAML:
<ListView ItemsSource="{Binding}"/>
The {Binding} is equivalent to {Binding .} which binds to the DataContext of the UserControl. If you need "more stuff" in your DataContext you can instead create a class like this:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
...
}
Use this class for the DataContext:
DataContext = new ViewModel();
And replace the binding to bind to the Images property:
<ListView ItemsSource="{Binding Images}"/>
Then you can add another property to ViewModel:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
public String Message { get { ... } set { ... } }
...
}
And bind it to a control:
<TextBlock Text="{Binding Message}"/>
Remember to fire the PropertyChanged event when the Message property is changed in ViewModel. This will update the UI when view-model properties are changed by code.

Resources