Xaml binding not working with Application.Current - c#

So I have two global properties Username and CurrentView defined in App.xaml.cs and im binding them in my Views.
App.xaml.cs
public partial class App : Application, INotifyPropertyChanged
{
public static object username;
public object Username { get { return username; } set { username = value; } }
public static Object currentView = new LoginView();
public object CurrentView
{
get { return currentView; }
set { currentView = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
And im binding both of them in different Views.
MainView.xaml
<Page Content="{Binding CurrentView, Source={x:Static Application.Current}}">
</Page>
LoginView.xaml
<TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" VerticalAlignment="Top" Width="165" Margin="218,159,0,0" Text="{Binding Path=Username, Source={x:Static Application.Current}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}" />
The binding in MainView is working perfectly fine. It is showing me LoginView which I have initialized CurrentView to in app.xaml.cs and it changes when the source is changed.
However username is not updating which is binded with the textbox in LoginView.
Cant seem to figure out the problem. It should update as Iv set the UpdateSourceTrigger to PropertyChanged.
Any help will be much appreciated :)

The Text property of the TextBox is of type string but your Username property is an object
Consider changing the Username property to string rather than object. If you need it to be an object, you need to have a converter in order for the binding to properly set it.
Also, looking at your binding I notice you have it set up as OneWay. Change that to TwoWay to ensure the Binding updates both ways from Source and Target.

Related

Binding model with multiple properties in UserControl using one DependencyProperty [duplicate]

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
Closed 4 years ago.
I would like to be able to bind complex model (many properties) to UserControl through DependencyProperty, and if model would be edited in UserControl I would like to see this edited information inside my binded model.
Example application: Model, UserControl (xaml + cs), MainWindow (xaml + cs). I have no ViewModel to simplify idea.
Model:
public class MyModel : INotifyPropertyChanged
{
private string _surname;
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged();
}
}
public string Surname
{
get => _surname;
set
{
_surname = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MyModelEditor.xaml (inside Grid):
<DockPanel>
<TextBox Text="{Binding MyModel.Name}"/>
<TextBox Text="{Binding MyModel.Surname}"/>
</DockPanel>
Also contains this line in UserControl root element:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
MyModelEditor.xaml.cs:
public partial class MyModelEditor : UserControl
{
public MyModel MyModel
{
get => (MyModel)GetValue(MyModelProperty);
set => SetValue(MyModelProperty, value);
}
public static readonly DependencyProperty MyModelProperty =
DependencyProperty.Register("MyModel", typeof(MyModel), typeof(MyModelEditor), new FrameworkPropertyMetadata(null));
public MyModelEditor()
{
InitializeComponent();
}
}
MainWindow.xaml (inside Grid):
<DockPanel>
<Button DockPanel.Dock="Bottom" Content="Press Me!" Click="ButtonBase_OnClick"/>
<controls:MyModelEditor MyModel="{Binding MyModel}"/>
</DockPanel>
MainWindow.xaml.cs:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private MyModel _myModel;
public MyModel MyModel
{
get => _myModel;
set
{
_myModel = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show(MyModel?.Name);
}
}
My test scenario: type text in textbox, press button.
Current behavior: Message after pressing button is empty.
Expected behavior: Message after pressing button is same like in textbox.
I wold not like to bind to all properties separately, because in future I will have much more then two properties.
Why current approach does not work?
How can I achieve my goal?
You are apparently not using the UserControl instance as Binding source in your UserControl's XAML. One way to do this would be to set the Binding's RelativeSource:
<TextBox Text="{Binding MyModel.Name,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
However, you don't need a new dependency property at all for this purpose. Just bind the UserControl's DataContext to a MyModel instance, like
<controls:MyModelEditor DataContext="{Binding MyModel}"/>
The Bindings in the UserControl's XAML would automatically work with the MyModel object, like this:
<DockPanel>
<TextBox Text="{Binding Name}"/>
<TextBox Text="{Binding Surname}"/>
</DockPanel>
For both of your TextBox controls, you should define their Binding with a TwoWay mode (ms docs on binding modes). Which, basically, would assure that the data flow is working in both direction (i.e. from the view model into the view and the other way around):
<DockPanel>
<TextBox Text="{Binding MyModel.Name, Mode=TwoWay}"/>
<TextBox Text="{Binding MyModel.Surname, Mode=TwoWay}"/>
</DockPanel>
As a good practice, you should always explicitly define what is the mode of the the Binding (NOTE: by default it's OneWay TwoWay - how to know which is the default?).
Another tip would be to go ahead and use MvvmHelpers nuget (github project), which could spare you the time of implementing INotifyPropertyChanged. Besides, you shouldn't re-invent the wheel
EDIT: Fixes are in your GitHub repo
Two things to note here
You have not instantiated your ViewModel (i.e. MyModel), so it was always null
You don't need to create DependencyPropery every time you want to pass some information to your UserControl. You could simply bind the DataContext itself

binding in wpf doesn't work, can't see why

I have a wpf app I'm using some xaml code that should let me view pdf files, I just started to use data biding and don't figure way this not works for me.
here m XAML:
<Grid>
<telerik:RadPdfViewerToolBar RadPdfViewer="{Binding ElementName=pdfViewer, Mode=OneTime}" SignaturePanel="{Binding ElementName=signaturePanel, Mode=OneTime}"/>
<telerik:SignaturePanel x:Name="signaturePanel" PdfViewer="{Binding ElementName=pdfViewer, Mode=OneWay}" Grid.Row="1"/>
<telerik:RadPdfViewer x:Name="pdfViewer" DocumentSource="{Binding Path=PathOfPdf, Mode=TwoWay}" DataContext="{Binding CommandDescriptors, ElementName=pdfViewer}" telerik:RadPdfViewerAttachedComponents.RegisterSignSignatureDialog="True" telerik:RadPdfViewerAttachedComponents.RegisterFindDialog="True" Grid.Row="2" telerik:RadPdfViewerAttachedComponents.RegisterSignaturePropertiesDialog="True" telerik:RadPdfViewerAttachedComponents.RegisterContextMenu="True"/>
<Grid>
And here code behind:
public partial class Page2 : Page, INotifyPropertyChanged
{
public Page2()
{
InitializeComponent();
DataContext = this;
}
private string _pathOfPdf= #"D:\MyFile.pdf";
public string PathOfPdf
{
get{ return _pathOfPdf; }
set{
if (_pathOfPdf != value)
{
_pathOfPdf = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
If I'm not using Biding it works fine. I if I do(on XAML):
DataContext="D:\MyFile.pdf" it shows the pdf
You need to either set the DataContext of the control or the source of the binding to the Page where the source property is defined:
<telerik:RadPdfViewer x:Name="pdfViewer"
DocumentSource="{Binding Path=PathOfPdf, RelativeSource={RelativeSource AncestorType=Page}}" />

binding instanciated property to UI

I have this class :
public class property : DependencyObject, INotifyPropertyChanged
{
private string _myproperty;
public string MyProperty
{
get
{
return this._myproperty;
}
set
{
this._myproperty = value;
NotifyPropertyChanged("MyProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string sproperty)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(sproperty));
}
}
}
In the main window I have created an instance of this class myclass xx = new myclass();, where I populate my property with string data and bind it to XAML like so:
<Window.Resources>
<local:property x:Key="prop"></local:property>
</Window.Resources>
In my TextBox i have set the binding :
Text="{Binding Path=MyProperty, Source={StaticResource prop}}" BorderBrush="#FFC7CACC" />
This will not work unless if i use the existing resources:
var property = (local:property)Resources["prop"];
Is there another way to update the TextBox rather than using the resources? I want to use the normal class instantiation.
if you say Text="{Binding Path=MyProperty, Source={StaticResource prop}}" BorderBrush="#FFC7CACC" />
means that your VM is an instance of property class.
Try to surround your textbox with a Grid and set the grid dataContext with an instance of your poperty clas.
I mean
<Grid DataContext="from view or from behind assign your vm= new property()">
<TextBox Text="{Binding Path=MyProperty" ....../>
</Grid>
Try this:
<Window.DataContext>
<local:property/>
<Window.DataContext>
<TextBox Text="{Binding MyProperty}"/>
After setting the data context, just try to build the application, the build will succeed if it can find the property class in the local namespace.
After building your app, if succeeded, you can try to set the binding and also the Intellisense will automatically show MyProperty in Binding Options.
If this doesn't work, try to set the data context and binding using the Properties panel. Maybe visually you can get things right.
Try it, and if it fails, tell me where it went wrong

NotifySourceUpdated & Binding.SourceUpdated not working

My event below (OnSourceUpdated) is not getting handled.
XAML:
<StackPanel x:Name="MyStackPanel"
Orientation="Horizontal"
DockPanel.Dock="Top">
<TextBox Text="{Binding Side, Mode=TwoWay}"
Width="100"/>
<TextBlock Background="Yellow"
Text="{Binding Side, Mode=OneWay,
NotifyOnSourceUpdated=True}"
Width="100"
SourceUpdated="OnSourceUpdated"
Binding.SourceUpdated="OnSourceUpdated"/>
</StackPanel>
C#:
....
MyStackPanel.DataContext = new MyItemClass() { Side = "Test" };
....
private void OnSourceUpdated(Object sender, DataTransferEventArgs args)
{
var i = args.Property;
}
public class MyItemClass : INotifyPropertyChanged
{
private string _side;
public string Side
{
get { return _side; }
set
{
_side = value;
OnPropertyChanged("Side");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
I have all the relevant settings done like NotifyOnSourceUpdated & SourceUpdated & Binding.SourceUpdated etc.
From msdn: Binding.SourceUpdated attached event occurs when a value is transferred from the binding target to the binding source, but only for bindings with the NotifyOnSourceUpdated value set to true
In the Binding of TextBlock, there is no value transfer from the binding target (TextBlock.Text) to the binding source (Side). Thus SourceUpdated cannot be fired.
Instead SourceUpdated can be fired on the first binding. Indeed here the target binding TextBox.Text can change the binding source (Side).
Maybe I'm missing something, but I'm thinking your approach to updating is a bit strange. Is there a reason you're not just going with
<TextBlock Text="{Binding foo, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" ... />
If you're just interested in updates coming from source, that's normally the way of doing it. Calling
OnPropertyChanged( "PropertyName" )
covers the rest.

Binding to change the property

Is it usable or this doesn't work: to change the Text Box.Text and the property behind to change can a binding of this type be made(i know that this can be made with an event from Text Box, i am looking for some kind of binding that can be made) ?
Should i just use Text Box.Text in my cod?
<TextBox Text="{Binding Path=NumeClient, Mode=TwoWay}" Height="23" HorizontalAlignment="Left" Margin="117,21,0,0" Name="textBox1" VerticalAlignment="Top" Width="249" />
public string NumeClient { get; set; }
If I understand the question correctly, you're asking how to setup a two way binding to the Text property of a TextBox?
<TextBox Text="{Binding Path=YourProperty, Mode=TwoWay}" />
This Makes both your property changes the TextBox and the TextBox changes the property (from MSDN)
Add in your class contructor DataContext = this;
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("PersonName");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
XAML :
<TextBox Text="{Binding Path=PersonName, Mode=TwoWay}" />
Hope it helps

Categories