I am doing TextBox binding as shown below. But the background color doesn't change. Any help is appreciated!
<TextBox x:Name="FirstNameTextbox" Text="Test" Background="{Binding Path=FirstNameBackground,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
C# code:
public SolidColorBrush FirstNameBackground
{
get
{
return firstNameBackground;
}
set
{
firstNameBackground = value;
OnPropertyChanged("FirstNameBackground");
}
}
A couple things.
A). Have you implemented INotifyPropertyChanged, without it binding to the xaml will only work one way and then stop, since there is no way to notify when property has changed.
B). Your xaml is not properly built. You must have a closing bracket at the end of textbox such as
<textbox/>
or
<textbox></textbox>
C). How is your datacontext set? If it is not set to anything , this will not work. This can be done by
a. datacontext =this in your codebehind
b. settings datacontext in xaml using window.datacontext as the xaml key
D). The information given is very very vague and i can only make decisions and suggestions based on common mistakes I have seen when building xaml/wpf apps. Please provide more information on
1. How datacontext is set.
2. How full xaml looks like
3. full codebeind/viewmodel if applicable
Related
I do have a ListDetailsView showing some data (lets say Company as a simple example here). Normally the details of a Company are shown as readonly. However, via the ListDetailsView.DetailsCommandBar it is possible to edit a Company (and also add a new Company). A clear separation between view and edit mode seems to be a good choice for the UI. I'm using a UserControl to show details of a Company.
So here are my questions:
Where should the differentiation between view- and edit-mode happen? I thought it is a good idea to have a CompanyDetailsControl and a CompanyDetailsEditControl and select between the two (both using the same CompanyDetailsViewModel). There are other solutions as well, for example, the CompanyDetailsControl could handle the edit- and view-mode internally.
Assuming that it is a good idea to switch between two UserControl, how can that be realized with the ListDetailsView.DetailsTemplate? I though it would be easy to use a DataTemplateSelector here, but that is only available for the ItemTemplate.
Not sure what code to provide to clarify my questions. So in case you need any code to better understand my question please leave a comment.
Note: I have never worked with UWP app, only applying MVVM pattern from WPF.
Straight line where the split should happen is not drawn. It often depends on the developer himself, which framework is used and more.
Me personally would go in way where UI handles UIs' things and ViewModel handles data only. That means the view is responsible for showing you the controls you are expecting to see/control the application. And when the view learns that property was changed, it should update how it looks.
Since the point we know the app will have edit & readonly modes, we should prepare all necessary UI components (UserControls, Pages, ...) to handle both those states. They would be binded to ViewModel that have base in BaseViewModel that already have this edit variable inside. So that each UI component know it can work with that.
Base view model:
abstract class BaseViewModel : INotifyPropertyChanged
{
private string mIsInEditMode;
public string IsInEditMode
{
get { return mIsInEditMode; }
set
{
if(mIsInEditMode == value) return;
mIsInEditMode = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(IsInEditMode));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
All "normal" ViewModels inherit from it:
class CompanyViewModel : BaseViewModel
{ /* props and logic of company */ }
UI component (UserControl) would have either trigger (<Style.Triggers/>) or binded properties Visibility and IsEnabled to the BaseViewModel. Those bindings would handle this logic of showing/hiding and you have potential to control whole layouts, hide controls etc.
<UserControl d:DataContext="{x:Bind local:CompanyViewModel}">
<UserControl.Resources>
<local:BoolInverterConverter x:Key="BoolInvert"/>
</UserControl.Resources>
<Grid>
<Grid IsVisible="{Binding IsInEditMode}" IsEnabled="{Binding IsInEditMode}">
<!-- Controls for edit mode -->
</Grid>
<Grid IsVisible="{Binding IsInEditMode, Converter={StaticResource BoolInvert}}"
IsEnabled="{Binding IsInEditMode, Converter={StaticResource BoolInvert}}">
<!-- Controls for readonly mode -->
</Grid>
</Grid>
</UserControl>
Note: I've used property IsVisible, You would actually use Visibility with some custom converter.
I'm fairly new to WPF. I have the following radio button in my application
<Viewbox Height="30">
<RadioButton Content="B1" GroupName="InputBelt" IsChecked="{Binding RBChecked, Mode=TwoWay, FallbackValue=True}" VerticalAlignment="Center"/>
</Viewbox>
<Viewbox Height="30">
<RadioButton Content="B2" GroupName="InputBelt" IsChecked="{Binding RBChecked, Converter={StaticResource boolconverter}, Mode=TwoWay}" VerticalAlignment="Center"/>
</Viewbox>
I have defined datacontext in xaml file
<Window.DataContext>
<vm:TestViewModel />
</Window.DataContext>
The issue is when the page is loaded for the 1st time, everything is fine. But when I go to some other page in the application and comes back to this page, the application crashes due to stackoverflow exception.
I even tried adding datacontext locally in radiobutton tag but it isn't working either.
Property structure given below.
private bool _bRBChecked;
public bool RBChecked
{
get { return _bRBChecked; }
set
{
_bRBChecked = value;
RaisePropertyChanged("RBChecked");
}
}
Upon investigating further, I found out that the RaisePropertyChanged of the binded property is being called too many times. This issue occurs only with the property binded to radio button. I have other controls which has two-way bind with other properties in the same page and it seems to work fine.
Now I have tried the below fix suggested in another stackoverflow question and it seems to be working.
set
{
if (_bRBChecked != value)
{
_bRBChecked = value;
RaisePropertyChanged("RBChecked");
}
}
But I would like to know the root cause of this issue and why the property is being set so many times and find any alternate fix if possible. Please let me know if I am missing anything.
Any insight is highly appreciable.
Your change notification is not protected from recursion. Property A changing Property B, whose change changes Property A...
A simple solution is this:
set
{
if(value != _bRBChecked){
_bRBChecked = value;
RaisePropertyChanged("RBChecked");
}
}
Simply check if the value is actually a change, before you go tell everyone about it. This pattern is explicitly used in the Examples. I am 90% sure the Depdency Properties have a similar recursion protection, but it would not be the first time I was wrong.
I think it is fairly easy to figure this out, based on the fix you shared.
What happens in steps:
You set the new value in one of the radio buttons
The event is raised
Since it's two way binding, the binding of the second radio button sets the value again to the other radio button
The event is raised again due to 3
Go back to 1 as now the value is set again for the first radio button.
With your fix the value is not set (the setter it's not called) so the event is not triggered again.
If it helps to know, I'm using Caliburn.Micro, and have laid out everything based on the MVVM framework requirements as I understand them.
Here is the relevant XAML ...
<ListView ItemsSource="{Binding ProductListBox}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding ProductID}"/>
<TextBlock Text="{Binding ProductDescription}"/>
<TextBlock Text="{Binding ProductDescriptionExtended}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The above content of the ListView is created dynamically based on the content of ProductListBox, which sits inside this class...
public class MainViewModel : Screen
{
public List<ProductModel> ProductListBox { get; private set; }
public void GetProductsButton()
{
DBAccess db = new DBAccess();
ProductListBox = db.GetProducts(SearchTextBox);
NotifyOfPropertyChange(() => ProductListBox);
}
}
That List is populated when a Button is clicked, it causes the above GetProductsButton() method to start.
Over in the DBAccess class, in the GetProducts method I would like to change the format of the text in the ProductListBox<>.ProductDescription. Its a string, but I'm happy to change it to any type should it help the cause!
The kind of change I would like to achieve is simply (highlighting) changing the background color of selected text based on found search terms the user had typed in, that the bound XAML TextBlock will then display.
What I cant work out is how to highlight any text at this time via C#, that will then be displayed purely by the bound XAML control... I've only been able to do it by hardcoding XAML which is not going to help for what I'm trying to achieve.
In the DBAccess class and inside the GetPeoducts method, this is the kind of thing I have tried in order to make this happen...
I've pasted in this code...
TextBlock textBlock1 = new TextBlock();
textBlock1.Inlines.Add(new Bold(new Run("TextBlock")));
textBlock1.Inlines.Add(new Run(" is designed to be "));
textBlock1.Inlines.Add(new Italic(new Run("lightweight")));
textBlock1.Inlines.Add(new Run(", and is geared specifically at integrating "));
textBlock1.Inlines.Add(new Italic(new Run("small")));
textBlock1.Inlines.Add(new Run(" portions of flow content into a UI."));
Which although doesn't change background color, will lead to that if I could make it work ;)
I think in every example of the kind of code I've quoted above, the writer always ends that code with something like this...
this.Content = textBlock1;
And the examples I've seen also always seem to inherit from the Window class. I don't want to do it this way. I want to find my search terms, highlight the given text and allow the bound TextBlock to update itself based on the populated List of type ProductModel.
I've tried changing the type of ProductDescription to a TextBlock, and used the above code to allow my XAML to remain bound directly to it
ie..
PM.ProductDescription = textBlock1;
In this case there was no GUI output at all for the content.
I've also tried straight HTML-like formatting to the string type
ie..
PM.ProductDescription = "<bold>Hello World</bold>";
This gives the literal text output of <bold>Hello World</bold> on the TextBlock control though.
I've also tried changing the type of Binding key from Text to other things in the hope I might work it out, without success.
Any ideas or help would be greatly appreciated, thank you!
Here is a little picture of what I would like to see the program be able to do..
I'm writing an app in XAML, and I'm using binding for getting values to the UI layer. I'd like to see what my control will look like while making changes to the XAML, but because the data values are bound, many areas show up as blank (which, in turn, messes up the relative layout).
Is there any way to give XAML values to use for rendering the control review without replacing the Binding directives?
You can also use design time data to see how your xaml works. You just need to add new class that will be treated as design time view model. Its more elegant way to test xaml at design time.
Maybe you can set TargetNullValue or FallbackValue property in your binding, example:
<TextBlock Text="{Binding NotExsitOrNullPropertyName, TargetNullValue=SomeDefaultValue, FallbackValue=SomeDefaultValue}" ></TextBlock>
Hope it hepls.
I've been playing around with WPF and MVVM and noticed a strange thing. When using {Binding ElementName=...} on a custom user control, the name of the root element within the user control seems to be visible in the window using the control. Say, here is an example user control:
<UserControl x:Class="TryWPF.EmployeeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TryWPF"
Name="root">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding}"/>
<Button Grid.Column="1" Content="Delete"
Command="{Binding DeleteEmployee, ElementName=root}"
CommandParameter="{Binding}"/>
</Grid>
</UserControl>
Looks pretty legit to me. Now, the dependency property DeleteEmployee is defined in the code-behind, like this:
public partial class EmployeeControl : UserControl
{
public static DependencyProperty DeleteEmployeeProperty
= DependencyProperty.Register("DeleteEmployee",
typeof(ICommand),
typeof(EmployeeControl));
public EmployeeControl()
{
InitializeComponent();
}
public ICommand DeleteEmployee
{
get
{
return (ICommand)GetValue(DeleteEmployeeProperty);
}
set
{
SetValue(DeleteEmployeeProperty, value);
}
}
}
Nothing mysterious here. Then, the window using the control looks like this:
<Window x:Class="TryWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TryWPF"
Name="root"
Title="Try WPF!" Height="350" Width="525">
<StackPanel>
<ListBox ItemsSource="{Binding Employees}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<local:EmployeeControl
HorizontalAlignment="Stretch"
DeleteEmployee="{Binding DataContext.DeleteEmployee, ElementName=root}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
Again, nothing fancy... except the fact that both the window and the user control have the same name! But I'd expect root to mean the same thing throughout the whole window XAML file, and therefore refer to the window, not to the user control. Alas, the following message is printed when I run it:
System.Windows.Data Error: 40 : BindingExpression path error:
'DeleteEmployee' property not found on 'object' ''String'
(HashCode=-843597893)'.
BindingExpression:Path=DataContext.DeleteEmployee;
DataItem='EmployeeControl' (Name='root'); target element is
'EmployeeControl' (Name='root'); target property is 'DeleteEmployee'
(type 'ICommand')
DataItem='EmployeeControl' (Name='root') makes me think that it treats ElementName=root as referring to the control itself. The fact that it looks for DeleteEmployee on string confirms that suspicion because string is exactly what the data context is in my contrived VM. Here it is, for the sake of completeness:
class ViewModel
{
public ObservableCollection<string> Employees { get; private set; }
public ICommand DeleteEmployee { get; private set; }
public ViewModel()
{
Employees = new ObservableCollection<string>();
Employees.Add("e1");
Employees.Add("e2");
Employees.Add("e3");
DeleteEmployee = new DelegateCommand<string>(OnDeleteEmployee);
}
private void OnDeleteEmployee(string employee)
{
Employees.Remove(employee);
}
}
It is instantiated and assigned to the window in the constructor, which is the only thing in code-behind for the window:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
This phenomenon prompts the following questions:
Is this by design?
If so, how is someone using a custom control supposed to know what name it uses internally?
If Name is not supposed to be used in custom control at all?
If so, then what are the alternatives? I switched to using {RelativeSource} in FindAncestor mode, which is working fine, but are there better ways?
Does this have anything to do with the fact that data templates define their own names copes? It doesn't stop me from referring to the main window from within a template if I just rename it so the name doesn't clash with the control.
Your confusion here about how wpf namescopes work is understanable in this situation.
Your issue is simply that you are applying a binding upon a UserControl, which is the "root" (so to speak) of its own namescope. UserControls, and pretty much any container objects, have their own namescopes. These scopes encompass not only child elements, but the object that contains the namescope as well. This is why you can apply x:Name="root" to your window and (except in this one case) locate it from a child control. If you couldn't, namescopes would be pretty much useless.
The confusion comes when you're acting upon a root of a namescope within an encompassing namescope. Your assumption was that the parent's namescope had precedence, but it does not. The Binding is calling FindName on the target object, which in your case is your user control. (Side note, the Binding isn't doing jack, the actual calls can be found in ElementObjectRef.GetObject, but that's where the Binding delegates the call to)
When you call FindName on the root of a namescope, only names defined within this scope are examined. Parent scopes are not searched. (Edit... a bit more reading of the source http://referencesource.microsoft.com/#PresentationFramework/src/Framework/MS/Internal/Data/ObjectRef.cs,5a01adbbb94284c0 starting at line 46 I see that the algorithm walks up the visual tree until it finds a target, so child scopes have precedence over parent scopes)
The result of all this is that you get the user control instance instead of the window, like you were hoping. Now, to answer your individual questions...
1. Is this by design?
Yep. Otherwise namescopes wouldn't work.
2. If so, how is someone using a custom control supposed to know what name it uses internally?
Ideally, you wouldn't. Just like you don't ever want to have to know the name of the root of a TextBox. Interestingly, though, knowing the names of templates defined within a control is often important when attempting to modify it's look and feel...
3. If Name is not supposed to be used in custom control at all?
If so, then what are the alternatives? I switched to using {RelativeSource} in FindAncestor mode, which is working fine, but are there better ways?
No! It's fine. Use it. If you aren't sharing your UserControl with other people, just make sure to change its name if you are experiencing this particular problem. If you aren't having any problem, reuse the same name all day, it isn't hurting anything.
If you ARE sharing your UserControl, you should probably rename it to something that won't conflict with other people's names. Call it MuhUserControlTypeName_MuhRoot_Durr or something.
4. If so, then what are the alternatives? I switched to using {RelativeSource} in FindAncestor mode, which is working fine, but are there better ways?
Nah. Just change the x:Name of your user control and move on.
5. Does this have anything to do with the fact that data templates define their own names copes? It doesn't stop me from referring to the main window from within a template if I just rename it so the name doesn't clash with the control.
No, I don't believe so. I don't think there is any good reason for it to be, anyhow.