I'm trying to bind a Textbox to a string defined in the .cs file using the followings:
Xaml Code:
<TextBox x:Name="textBox_Data" CaretBrush="DodgerBlue" Foreground="White" Text="{Binding Data}" HorizontalAlignment="Left" Height="22" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="123" SelectionChanged="textBox_Data_SelectionChanged"/>
Xaml.cs Code:
public string Data{get; set;}
But the string isn't updating...
Your class has to derive from INotifyPropertyChanged and you have to implement it in your property setter
Or more pleasant way: Install PropertyChanged.Fody from nuget. You can read more about it here: https://github.com/Fody/PropertyChanged
And keep in mind, not to use this.DataContext=this; when initializing your window, use binding as dovid suggests.
{Binding Data} refer the DataContext of current element (or to one of ancestors).
one way
for refer the xaml.xxx.cs you need refer the Window element, you can give him a name:
<Window x:Name="window" x:Class=...
and change the Binding to refer element name:
Text="{Binding Data, ElementName=window}"
Second way
you can also inject all window class to current DataContext:
<Window DataContext="{Binding RelativeSource={RelativeSource Self}}" x:Class=...
Now you can leave the original expression:
Text="{Binding Data}"
Third way
You can also set DataContext from the code. do not change anything in xaml, and add in this line (DataContext = this;) at end of constructor:
public xyz() {
InitializeComponent();
//...
DataContext = this;
}
Related
I have the following xaml view:
<UserControl x:Class="MyViews.PersonView"
xmlns:views="clr-namespace:MyViews"
[...]
>
[...]
<dxb:BarManager x:Name="MainBarManager">
<dxb:BarManager.Items>
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
Command="{Binding PrintPersonsCommand}"
CommandParameter="{Binding PersonsCardView, ElementName=CardUserControl}"
/>
</dxb:BarManager.Items>
<Grid>
<Grid.RowDefinitions>
[...]
</Grid.RowDefinitions>
<views:CardView x:Name="CardUserControl" Grid.Row="2"/>
</Grid>
[...]
</UserControl>
The CardView is defined as follows:
<UserControl x:Class="MyViews.CardView"
[...]>
[...]
<dxg:GridControl ItemsSource="{Binding Persons}" SelectedItems="{Binding SelectedPersons}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionMode="MultipleRow">
[...]
<dxg:GridControl.View>
<dxg:CardView x:Name="PersonsCardView"
[...]
CardTemplate="{StaticResource DisplayCardTemplate}"
PrintCardViewItemTemplate="{StaticResource PrintCardTemplate}"/>
</dxg:GridControl.View>
[...]
</dxg:GridControl>
</UserControl>
The PrintPersonsCommand is defined as follows in my ViewModel:
public class PersonViewModel
{
public PersonViewModel(...)
{
[...]
PrintPersonsCommand = new Prism.Commands.DelegateCommand<DataViewBase>(PrintPersons, CanPrintPersons);
}
public Prism.Commands.DelegateCommand<DataViewBase> PrintPersonsCommand { get; private set; }
private void PrintPersons(DataViewBase view)
{
_printService.ShowGridViewPrintPreview(view);
}
private bool CanPrintPersons(DataViewBase view)
{
return true;
}
}
Now, when I click the Print button, the above PrintPersons method is always fed with null. How do I pass CardUserControl.PersonsCardView in my MyViews.PersonView xaml above, how do I pass that PersonCardView to my command? In other words, how do I fix
CommandParameter="{Binding PersonsCardView, ElementName=CardUserControl}"
to make it work?
Currently, the only solution I've found to this problem is to replace the Command and CommandParameter with
ItemClick="OnPrintBtnClick"
and then in the PersonView's code-behind file to do:
private void OnPrintBtnClick(object sender, ItemClickEventArgs e)
{
var ctxt = DataContext as PersonViewModel;
ctxt.PrintPersonsCommand.Execute(CardUserControl.PersonsCardView);
}
That works but I can't believe there is no other way. I'm not happy with that solution because I don't have the benefits of using the Command any more, like e.g. the automatic evaluation of the Command's CanExecute method. I could also put the CardView's xaml code in the PersonView.xaml but I like my controls to be in separate files because I have the feeling it's more structured and each user control has its own responsibilities which can nicely be split into separate files. Also, that solution binds my view to my view model too tightly.
Can someone help me out please?
Without changing your existing view and viewmodel hierarchy, I was able to pass the GridControl.View to the PersonViewModel using the Tag property
You can assign the CardView to the Tag property at the bottom of your CardView UserControl, and then access this Tag as CommandParameter.
CardView UserControl
<UserControl x:Class="MyViews.CardView"
[...]>
[...]
<dxg:GridControl ItemsSource="{Binding Persons}" SelectedItems="{Binding SelectedPersons}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionMode="MultipleRow">
[...]
<dxg:GridControl.View>
<dxg:CardView x:Name="PersonsCardView"
[...]
CardTemplate="{StaticResource DisplayCardTemplate}"
PrintCardViewItemTemplate="{StaticResource PrintCardTemplate}"/>
</dxg:GridControl.View>
[...]
</dxg:GridControl>
<UserControl.Tag>
<Binding ElementName="PersonsCardView"/>
</UserControl.Tag>
</UserControl>
Print Button Xaml:
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
Command="{Binding PrintPersonsCommand}"
CommandParameter="{Binding ElementName=CardUserControl, Path=Tag}"
/>
Based on the valuable input of Insane, I came up with the following two cleaner fixes:
Code-behind solution
In the PersonView, use the ItemClick event handler on the Print button:
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
ItemClick="OnPrintBtnClick"/>
Adapt the corresponding code-behind file like this:
public partial class PersonView : UserControl
{
readonly IPrintService _printService;
public PersonView(IPrintService printService)
{
_printService = printService;
InitializeComponent();
}
private void OnPrintBtnClick(object sender, ItemClickEventArgs e)
{
_printService.ShowGridViewPrintPreview(CardUserControl.PersonsCardView);
}
}
Because I want to gray-out the Print button when there is no selection, I still need to add some code to make that happen. I can get it by
1. updating the button code to
<dxb:BarButtonItem x:Name="bbiPrint"
Content="{Binding Print, Source={StaticResource CommonResources}}"
ItemClick="OnPrintBtnClick" IsEnabled="{Binding CanPrintPersons}"/>
refreshing the CanPrintPersons property in the PersonViewModel upon Persons selection change
That's it.
CardViewModel solution
In that solution, we have a PersonView with its underlying PersonViewModel and a CardView with its underlying CardViewModel. I will not describe that solution with all the details as it is overkill in my situation but for the sake of completeness, I'll give the main points. Upon clicking the Print button on the PersonView, the PersonViewModel's PrintCommand is called. That command emits a Print event to the CardViewModel which in turn calls its own PrintCommand. That latter command calls
_printService.ShowGridViewPrintPreview(View);
where the View is a CardViewModel's property that is set upon CardView loading with e.g.
<dxmvvm:Interaction.Behaviors>
<dxmvvm:EventToCommand EventName="Loaded" Command="{Binding ViewLoadedCommand}" CommandParameter="{Binding ElementName=PersonsCardView}" />
</dxmvvm:Interaction.Behaviors>
Because I have two child views I want to print, I'd need to add a view model for each one of those. In addition, those two view models plus the PersonViewModel need access to the list of Persons to be printed. In particular, they need a shared access to the same data, so that they are synchronized. A simple way to do that is explained here and is totally doable. But I think it is not worth the trouble for the simple use case I have as it adds more complexity than necessary.
I got ListBox with DataTemplate, inside DataTemplate I got another ListBox, trying to bind it's Visibility to another object which is found in the MainPage
XAML:
<ListBox x:Name="RegistersListView" ItemsSource="{x:Bind registersList}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="structures:Register">
<StackPanel>
<ListBox x:Name="FieldsListView" ItemsSource="{x:Bind fields_list}" Visibility="{x:Bind SomeVisibilityObjectIMain}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="structures:Field">
<Button Content="{x:Bind name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#:
public sealed partial class HWTab : Page
{
public ObservableCollection<Register> registersList = new ObservableCollection<Register>();
public var SomeVisibilityObjectIMain;
public HWTab()
{
InitializeComponent();
InitData();
this.DataContext = hwType;
}
....
}
I need to bind to "SomeVisibilityObjectIMain" somehow, I tried to bind with ElementName or even make object static, but could not succeed.
My bindable object is more complex than the example here but solve this will give me the way for solution.
You could use {Binding} instead of x:Bind. This way you could add a x:Name="Page" to your page and then use this name in the inner binding:
{Binding ElementName=Page, Path=MyProperty}
For {Binding} to work however, MyProperty must be actually a property. From your sample code (which uses var which is also invalid) it seems it is just a plain field, so you will need something like:
public string MyProperty {get;set;}
To also get PropertyChanged notifications, you will need to add a backing field and trigger PropertyChanged event.
However, overall a better solution would be to include all information a DataTemplate needs into the actual items which are bound to it. That means - you would create a custom view model type for the items, which would include the information that you need to control visibility.
I have:
<Page.Resources>
<data:PublishManager x:Key="pubManager"/>
</Page.Resources>
then in my textBlock i used this:
<TextBlock Grid.Row="2" Canvas.ZIndex="3" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Path=SocialStatus, Mode=TwoWay, Source={StaticResource pubManager}}"></TextBlock>
my class PublishManager look like this:
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _SocialStatus;
public string SocialStatus
{
get
{
return _SocialStatus;
}
set
{
_SocialStatus = value;
RaisePropertyChanged("SocialStatus");
}
}
why when i write in my method code something like this it's don't work for me?
SocialStatus = "StackOverflow";
Why my page with TextBlock don't refresh content?
The problem is that you are using static resource in your binding scenario. Static resources aren't monitored in case of property changes. Do you really need to use your PublishManager as page resource?
It would be better when an instance of PublishManager will be set as DataContext.
So firstly set Page.DataContext:
<Page.DataContext>
<data:PublishManager/>
</Page.DataContext>
And later bind to context property:
<TextBlock Grid.Row="2" Canvas.ZIndex="3" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding SocialStatus}"/>
Do not use the StaticResource here. They are used where resource value is not likely to change. Read below links:
StaticResource
using StaticResource
if the value of SocialStatus is always going to be StackOverflow then declare the static property with that value and your binding should work. else you have to create an object of the class and give it as a datacontext to the view.
Or just create a datacontext when required
<TextBlock Grid.Row="2" Canvas.ZIndex="3" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Path=SocialStatus, Mode=TwoWay}">
<TextBlock.DataContext>
<data:PublishManager/>
</TextBlock.DataContext>
</TextBlock>
this will work too.
https://msdn.microsoft.com/en-us/library/cc838207%28v=vs.95%29.aspx
binding to a static source works. You just have to make sure data: maps to right namespace. As you did not provide the complete Xaml. you might want to check this.
<UserControl x:Class="PublishManager.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:namespace-to-code">
Although best practice is to use the DataContext to make it more reusable. Like the answer of Patryk provided.
I have the following xaml:
<Window x:Class="Retail_Utilities.Dialogs.AdjustPriceDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ShowInTaskbar="False"
WindowStartupLocation="CenterOwner" Name="Adjust_Price"
Title="Adjust Price" Background="#ee0e1c64" AllowsTransparency="True" WindowStyle="None" Height="330" Width="570" KeyDown="Window_KeyDown" Loaded="Window_Loaded">
<Grid Height="300" Width="550">
<ListBox HorizontalAlignment="Right" Margin="0,110,35,60" Name="lstReasons" Width="120" VerticalAlignment="Stretch"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window, AncestorLevel=1}, Path=reasons}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=POS_Price_Change_Reason}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Here is the relevant c#:
namespace Retail_Utilities.Dialogs
{
public partial class AdjustPriceDialog : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Twr_POS_Price_Change_Reason> reasons; ...
and finally, here is the code from another page that opens this window:
AdjustPriceDialog apd = new AdjustPriceDialog();
apd.Owner = (Window)this.Parent;
apd.reasons = new ObservableCollection<Twr_POS_Price_Change_Reason>();
var pcr = from pc in ctx.Twr_POS_Price_Change_Reasons where pc.Deactivated_On == null select pc;
foreach (Twr_POS_Price_Change_Reason pc in pcr)
{
apd.reasons.Add(pc);
}
apd.AdjustingDetail = (Twr_POS_Invoice_Detail)lstDetails.SelectedItem;
if (apd.ShowDialog() == true)
{
}
When the dialog box opens, my lstReasons list is empty. I don't get any errors and when I place a stop in the code, I see that the reasons collection gets populated with the items from the table.
Reasons needs to be a Property (add { get; set;} ). Also, look at Visual Studio Output - it shows Binding errors, there should be some info about failed binding to reasons.
The problem seems to be How you are creating the property.
I know you put your prperty as an observable collection but this doesn't mean it is by it self observalble!
so you need to notify the UI when this property is changed by doing something in the setter like this:
public ObservableCollection<Twr_POS_Price_Change_Reason> reasons
{
get{....}
set
{
Notify('reasons')
}
}
I don't remember the exact code because I didn't use WPF for a while but it is a method in INotifyPropertyChanged, good luck!
It seems your binding path is set to POS_Price_Change_Reason, while the name of your property is reasons. Unless you didn't include POS_Price_Change_Reason in your example code and reasons is the backing field for this property.
Also, keep in mind that you can only bind to public properties, not fields. Additionally, if you change the value of the property, you need to notify the view of this change, by invoking your PropertyChangedEventHandler event for that property:
PropertyChanged(new PropertyChangedEventArgs("YourPropertyName"));
I want to use a color picker in my wpf application and I saw a nice looking one on this codeproject page. The control works fine until I want to connect the control to a viewmodel.
I created a small test program with this viewmodel:
public class ColorViewModel : ViewModelBase
{
public ColorViewModel()
{
LineColor = Brushes.Yellow;
}
SolidColorBrush _brushColor;
public SolidColorBrush LineColor
{
get { return _brushColor; }
set
{
_brushColor = value;
RaisePropertyChanged(() => LineColor);
}
}
}
The test program has a textbox and the colorpicker controls:
<StackPanel Orientation="Horizontal">
<TextBlock Text="Please Select a Color" FontWeight="Bold" Margin="10"
Foreground="{Binding Path=LineColor, UpdateSourceTrigger=PropertyChanged}"/>
<vw:ColorPickerControlView x:Name="ForeColorPicker" Margin="10"
CurrentColor="{Binding Path=LineColor, UpdateSourceTrigger=PropertyChanged }"/>
</StackPanel>
In the loaded event of the main window in my test application I set the viewmodel to the datacontext like this:
DataContext = new ColorViewModel();
The problem is that I can't seem to bind the LineColor property of the viewmodel to the CurrentColor property of the ColorPickerControlView. The CurrentControl property of the ColorPickerControlView seems to be fine. The constructor looks like this:
public ColorPickerControlView()
{
this.DataContext = this;
InitializeComponent();
CommandBindings.Add(new CommandBinding(SelectColorCommand, SelectColorCommandExecute));
}
In the constructor of the UserControl there is the line this.DataContext = this; I read that is is necessary to bind the dependency properties. Do I override this line when I set my viewmodel to the datacontext and is that why I can't bind to the CurrentColor property? Is there any workaround? Or did I make another mistake?
You are right in thinking that the DataContext=this phrase in the UserControl's constructor preempts if from binding to an external viewmodel. It was disccussed in this question. This is easily remedied however. There is only one DependencyProperty in the UserControl's code behind that the xaml binds to: CurrentColor.
Do this:
Add a Name="Root" attribute to the UserControl tag of the
UserControl's xaml
Change the attribute (of the Border tag)
Background="{Binding
Path=CurrentColor}" to:
Background="{Binding
ElementName=Root,
Path=CurrentColor}"
Remove the offending DataContext=this
line from the UserControl's
constructor!
That should be all that there is to it. I wrote a proof of concept that demonstrates the above. If you like I can post it, but the code above should be all you need.
Both binding must be clashing the set the value of the property. Try Setting the Mode=OneWay
<StackPanel Orientation="Horizontal">
<TextBlock Text="Please Select a Color" FontWeight="Bold" Margin="10"
Foreground="{Binding Path=LineColor, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
<vw:ColorPickerControlView x:Name="ForeColorPicker" Margin="10"
CurrentColor="{Binding Path=LineColor, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay }"/>
</StackPanel>
The line this.DataContext = this isn't really needed since you are replacing the DataContext with an instance of the ViewModel. You also do not need to assign the DataContext on the Loaded event handler. Just set it on the constructor. You can set it after the call to InitializeComponent method.
Remove the line DataContext = this in file ColorPickerControlView.xaml.cs
Change the Binding in ColorPickerControlView.xaml to Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type CustomWPFColorPicker:ColorPickerControlView}},
Path=CurrentColor}"