Can anyone shed some light as to why the following code is not working? By "not working" I mean the image is not rendered in the Panorama control:
XAML
DataContext="{Binding RelativeSource={RelativeSource Self}}"
shell:SystemTray.IsVisible="False">
<Grid x:Name="LayoutRoot">
<controls:Panorama Title="My Control">
<controls:Panorama.Background>
<ImageBrush ImageSource="{Binding RandomImage}"/>
</controls:Panorama.Background>
C#
public string RandomImage { get; set; }
Note: The RandomImage property is set to a public jpg image on the internet.
EDIT
I have also tried to change the RandomImage property to ImageSource but did not have any luck with that.
I'm gonna hazard a guess that you're setting RandomImage at some point after the page loads, which means that the binding has already been checked. You need to implement INotifyPropertyChanged and call your PropertyChanged event in the setter for RandomImage. For a detailed explanation of this, check out this MSDN article.
The long and short of it is that the binding is checked when the page loads and then not again unless something triggers it. Implementing INotifyPropertyChanged means that when you call your PropertyChanged event, it notifies the UI to check the binding again and see what's new so it can update itself.
Related
In a UWP Windows 10 application I'm working on have a command in my Viewmodel
public class ViewModel {
public ICommand LoadedCommand{ get; set; }
}
and I'd like to run it when my grid control gets the focus, without having to call the command in the code behind file.
<Grid GotFocus="Grid_GotFocus">
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
</Grid>
Does anyone know if its possible to assign the command to the event directly in the xaml and if it is how I might do it.
Many Thanks
You can use either the InvokeCommandAction from the XAML Behavior library, or use x:Bind to event that's available in the Anniversary Update. So you can have something like -
GotFocus="{x:Bind Vm.OnGridGotFocus}"
Note the OnGridGotFocus() here is a method and it can either have no parameters or matdch the signiture of the event.
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.
I am fairly new to Xaml.
I am learning UWP(Universal Windows Platform) and I have more buttons and I want to bind their Background property to a property of a ViewModel that will change during some events.
I implemented the INotifyPropertyChanged and everything works ok (the color of the buttons change) when I bind the Background property in the Buttons' declaration in XAML:
<Button Content="0" Grid.Column="0" Grid.Row="5"
Background="{Binding ButtonColor, Source={StaticResource AppViewModel}}" Style="{StaticResource BasicButton}"/>
StaticResource AppViewModel is a resource in App.xaml:
<Application.Resources>
<viewModel:AppViewModel x:Key="AppViewModel" />
</Application.Resources>
I don't know how ok is to declare a ViewModel for App.xaml, but it's a solution I found for having global variables (the variables are held inside the viewModel).
Now back to my question:
As I don't want to bind the Background on every single button, I tried to add it on the style like this:
<Style x:Key="BasicButton" TargetType="Button">
<Setter Property="Background" Value="{Binding ButtonColor, Source={StaticResource AppViewModel}}" />
</Style>
But now when the color variable is changing during running the app, the UI doesn't update anymore.
It seems that binded properties in styles don't respond to changes of variables.
What am I doing wrong?
Thank you for any answers.
After more searching I found a video from Jerry Nixon : http://blog.jerrynixon.com/2013/01/walkthrough-dynamically-skinning-your.html
It seems that because we don't have DynamicResource in uwp / winrt, we have to do a trick:
We renavigate to the same frame. So after we change the property, we have to do something like this:
var frame = Window.Current.Content as Frame;
frame.Navigate(frame.Content.GetType());
frame.GoBack();
It's like invalidating a control in Windows Forms. It's making the UI redraw itself.
I'm not sure if this has side effects, I'll have to dig more. I'll come back if I find any.
I'm quite new to MVVM, and I've been constructing my ViewModels. I have a ViewModel which contains an ICommand, which is then bound to in my View by a command button. The ICommand causes a procedure to be invoked on my ViewModel which then invokes a further large slow procedure. While this procedure is happening I want to make a control/UIElement's visibility to become visible and then hidden after the procedure has finished (I intend to bind a label and indeterminate progress bar's visibility)
For example, in my view model I have
public void calledFromCommandButton() {
RaisePropertyChange("Starting");
superLongProcedure();
RaisePropertyChange("Finished");
}
This just feels a bit silly though, having to raise 2 different property changes and hence, I presume I'm doing it all wrong. I think I could do it with one property change along with a convertor?
So, what is the proper and correct method to bind UIElement visibilities to property change events?
Thanks
Thomas
I would recommend using a single boolean property (IsWorking or something) and then using the BooleanToVisibilityConverter to show and hide the button. So, it would look something like:
<Window ...>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="TrueToVisibleConverter"/>
</Window.Resources>
...
<Button x:Name="CancelButton" Content="Cancel" Visiblity="{Binding IsWorking, Converter={StaticResource TrueToVisibleConverter}}"/>
...
</Window/>
I've been Google'ing a bit around for this, but without getting any real answer. Probably because my question might be a bit cryptic. Here goes:
Lets say i have an ObservableCollection<SomeModel> containing a bunch of models. I then add the corresponding Views to a Canvas. Specifying this in the resources of the Window, and then bind the Canvas' ItemsSource to the ObservableCollection<SomeModel>. This works fine.
SomeModel is bound to SomeView, this is a UserControl.
Now, when this View gets focus, or when I MouseDown on it, I would like to have it marked as "Selected". Somehow, i would like to have a property in the codebehind to the Window holding my Canvas, where i can always get the selected item.
I've been thinking of having a BindingList instead of the ObservableCollection, and when an IsSelected property on the model changes, then a method will extract the selected item from the list. But this seems to be a bit of a performance killer, as i will be notified on all changes to the items.
How can I accomplish this?
There are multiple ways you could solve this. But probably the simplest it to work with a ListBox and bind to it. ListBox as it has bindable ItemsSource and a SelectedItem property that has the item that is currently selected in it. It also calls the SelectionChanged event when the selection changes if you want to do something in the code behind .cs file.
I would recommend keeping the ObservableCollection in your View Model to keep true to MVVM.
If the style or placement of ListBox does not suit your override the template to something that suits your needs better.
You could look at a behaviour if the above doesn't work for you but best to keep it simple.
UPDATE
This is how you would build the view. Note the ItemsPanel attribute is bound to a defined ItemsPanelTemplate in the UserControl.Resources section which specifies a Canvas to put the items on.
<UserControl
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"
xmlns:local="clr-namespace:SilverlightApplication1"
x:Class="SilverlightApplication1.View1"
d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<local:View1Model x:Key="View1ModelDataSource" />
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<Canvas />
</ItemsPanelTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource View1ModelDataSource}}">
<ListBox Margin="80,85,183,54" ItemsPanel="{StaticResource ItemsPanelTemplate1}" ItemsSource="{Binding DataModelCollection}"/>
</Grid>
On the View Model
public class View1Model
{
private ObservableCollection<SomeModel> _DataModelCollection;
public ObservableCollection<SomeModel> DataModelCollection
{
get { return this._DataModelCollection; }
set { this._DataModelCollection = value; }
}
}
It should be noted though that Canvas itself does not have any logic to let the user move controls around on it at runtime.