I need to bind to static properties in my App.xaml.cs class and have so far done this using:
Property="{Binding Source={x.Static Application.Current}, Path=SomePath}"
This works OK when the application is being run directly, but when the application is started from another application, these bindings don't work as I believe Application.Current then points at the parent application rather than the application that the xaml sits under.
How would I bind to the immediate App.xaml.cs file properties rather than those from the parent application?
Hope that makes sense!
So one solution I've found so far is to put a class between App.xaml.cs and the XAML I'm trying to bind:
App.xaml.cs:
public partial class App : Application
{
public static string SomeText;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
SomeText = "Here is some text";
}
}
MyProperties.cs:
public class MyProperties
{
public static string SomeText
{
get { return App.SomeText; }
}
}
MainWindow.xaml:
<Window.Resources>
<local:MyProperties x:Key="properties"/>
</Window.Resources>
<Grid>
<TextBlock Text="{Binding Source={StaticResource properties},
Path=SomeText}"/>
</Grid>
Any other suggestions are still more than welcome :)
App.xaml.cs:
public partial class App : Application
{
public static string SomeText => "Here is some text";
}
MainWindow.xaml:
<Grid>
<TextBlock Text="{Binding Source={x:Static Application.Current},
Path=SomeText}"/>
</Grid>
Related
I have been reading about MVVM (and MVVM Light) lately, so tried to implement in an application with 2 ViewModels.
When I use the ViewModelLocator in the datacontext the Command binding does not work, if I bind the ViewModel to the datacontext of the ViewModel itself it works!
What am I missing here?
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MotionViewModel>();
SimpleIoc.Default.Register<LiveViewViewModel>();
}
public LiveViewViewModel liveViewViewModel
{
get
{
return ServiceLocator.Current.GetInstance<LiveViewViewModel>();
}
}
public MotionViewModel motionViewModel
{
get
{
return ServiceLocator.Current.GetInstance<MotionViewModel>();
}
}
public static void Cleanup()
{
ClearLiveViewViewModel();
ClearMotionViewModel();
}
public static void ClearLiveViewViewModel()
{
ServiceLocator.Current.GetInstance<LiveViewViewModel>().CloseCamera();
ServiceLocator.Current.GetInstance<LiveViewViewModel>().Cleanup();
}
public static void ClearMotionViewModel()
{
ServiceLocator.Current.GetInstance<MotionViewModel>().Cleanup();
}
}
This is the ViewModel code:
public class MotionViewModel : ViewModelBase
{
private RelayCommand _mocoConnectCommand;
public MotionViewModel()
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
}
else
{
// Code runs "for real"
Task.Factory.StartNew(() => Initialize());
}
}
public RelayCommand MoCoConnectCommand
{
get;
private set;
}
private void Initialize()
{
MoCoConnectCommand = new RelayCommand(MoCoConnect);
}
private void MoCoConnect()
{
MessageBox.Show("Connection button pressed");
}
#endregion
}
This is the XAML View Code:
<UserControl.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource Locator}" />
</UserControl.DataContext>
<Button Style="{DynamicResource MahApps.Metro.Styles.MetroButton}"
Grid.Column="0" Grid.Row="0"
Height="30" Width="28" Margin="-1,2,1.333,2.667"
Command="{Binding MoCoConnectCommand}" >
<iconPacks:FontAwesome Kind="LinkSolid"/>
</Button>
The user control's data context is a ViewModelLocator and the button is binding to MoCoConnectCommand property. But the class ViewModelLocator don't have the property MoCoConnectCommand.
I think you need inject MotionViewModel in the user control's data context, like :
<UserControl.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource Locator}" Path="motionViewModel" />
</UserControl.DataContext>
If you want to get into this, try avoiding external libraries. They just make things confusing.
The thing to understand is when you are writing SomeProperty="{Binding PropertyFromViewModel}" it is binding to that usercontrol's DataContext.
How to set the datacontext?
simple example:
Write this(your file names replace the example ones) into your App.xaml.cs(override onstartup method) and remove "StartupUri" from App.xaml
Window w = new Window();
w.DataContext= new SomethingViewModel();
w.Show();
Now you can bind to public properties from SomethingViewModel in you Window.xaml
What else do you need:
-Implementation of INotifyPropertyChange in a base class(ViewModelBase or whatever name you want) you can keep inheriting for viewmodels and objects that need to refresh the UI
-Implementation of RelayCommand. This can be used to create commands in any viewmodel that can be bound with Binding to commands of controls for example a Button.
Edit: the other answer should fix your problem if you insist on using 3rd party libraries
I try to use binding to display Hi in the Text content.
However, when clicking the button, it doesn't work.
Could someone help me to solve the problem?
Thanks.
1.XAML CODE :
<Window x:Class="Wpftest.binding.Window0"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window0" Height="300" Width="300">
<Grid>
<TextBox x:Name="textBox2" VerticalAlignment="Top" Width="168"
Text="{Binding Source= stu, Path= Name, Mode=TwoWay}"/>
</Grid>
</Window>
2.Class :
namespace Wpftest.binding.Model
{
public class student : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return name; }
set { name = value;
if(this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new
PropertyChangedEventArgs("Name"));
}
}
}
}
}
3.XAML.cs:
namespace Wpftest.binding
{
public partial class Window0 : Window
{
student stu;
public Window0()
{
InitializeComponent();
stu = new student();
}
private void button_Click(object sender, RoutedEventArgs e)
{
stu.Name += "Hi!";
}
}
}
There are many ways to achieve what you need; the correct method depends very much on what style of application you want to create. I'll demonstrate two methods that will require minimal changes from your supplied example:
Method 1
Set the DataContext to stu and bind to the Name property.
XAML.cs
private student stu;
public Window0()
{
InitializeComponent();
stu = new student();
DataContext = stu;
}
XAML code
<TextBox Text="{Binding Path=Name, Mode=TwoWay}"/>
Method 2
Generally you will set the DataContext to some object other than the Window (e.g. the ViewModel if you are following the MVVM pattern), but sometimes you may need to bind a control to some property of the Window. In this case the DataContext can't be used, but you can still bind to a property of the Window by using RelativeSource. See below:
XAML.cs
// note this must be a property, not a field
public student stu { get; set; }
public Window0()
{
InitializeComponent();
stu = new student();
}
XAML code
<TextBox Text="{Binding Path=stu.Name, Mode=TwoWay,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
Hint: if you are having trouble with WPF data binding, then it often helps to look at the debugger output window to see the binding trace messages. And debugging can be further enhanced by adding this namespace to the Window element
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
and then setting the TraceLevel e.g.
<TextBox Text="{Binding Source=stu, diag:PresentationTraceSources.TraceLevel=High}"/>
Basically you need to set DataContext property to your Window.
For example:
public MainWindow()
{
DataContext=new YourViewModel();
}
DataContext of Window is a way to communicate between View(XAML) and ViewModel(C# code)
In addition, you can add DataContext in xaml:
<Window.DataContext>
<local:YourViewModel/>
</Window.DataContext>
Also, instead of handling Click event, you should use Command property of Button. Example can be seen here.
I am trying to learn the Prism Navigation support. Presently, I have a prism Region and I want to load view to that region using RegionManager.RequestNavigate(). The navigation does occur, however the IsNavigationTarget() of INavigationAware is not invoked, even if the ViewModel of the Navigation Target view implements INavigationAware interface. Here is the code that I am using.
Shell:
<StackPanel Margin="10">
<TextBlock Text="Main Window"/>
<Button Content="RegionA" Command="{Binding NavigateToACommand}" />
<ContentControl prism:RegionManager.RegionName="MainRegion"/>
</StackPanel>
ShellViewModel:
private void NavigateToA () {
Uri uri = new Uri("RegionAView", UriKind.Relative);
RegionManager.RequestNavigate("MainRegion", uri);
}
RegionAView:
<UserControl x:Class="NavigationExample.RegionAView"
<Grid>
<TextBlock Text="This is Region A"/>
</Grid>
</UserControl>
RegionAViewModel
public class RegionAViewModel : INavigationAware{
public RegionAViewModel() {
}
public bool IsNavigationTarget(NavigationContext navigationContext) {
return false; //Not Invoked
}
public void OnNavigatedTo(NavigationContext navigationContext) {
//Gets Invoked
}
}
RegionAView.xaml.cs
[Export("RegionAView")]
public partial class RegionAView : UserControl {
public RegionAView() {
InitializeComponent();
}
}
Why does the IsNavigationTarget() not getting invoked prior to completion of Navigation?
I think your problem is that you export your view as singleton. modify VM and V as follow:
[Export("RegionAView")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class RegionAView : UserControl
{
public RegionAView()
{
InitializeComponent();
}
}
Basically, IsNavigationTarget will be invoked when you have existing instances. But it will not work for newly created instance.
I am having trouble getting the following scenario to work (this code is not the actual code but the principals are the same. Basically I need to pass a value down from a MainPage down to a nested "reusable user control" that binds to it's own properties. I want to see the "This is it!" text on the screen but it's not being set in the SilverlightControl2 control (I suspect due to the setting of the DataContext) - but I how do I fix it?
MainPage.xaml
<Grid>
<ContentPresenter>
<ContentPresenter.Content>
<Local:SilverlightControl1 OneValue="This is it!"/>
</ContentPresenter.Content>
</ContentPresenter>
</Grid>
SilverlightControl1.xaml
<Grid>
<Local:SilverlightControl2 TwoValue="{Binding OneValue}"/>
</Grid>
SilverlightControl1.xaml.cs
public partial class SilverlightControl1 : UserControl
{
public string OneValue
{
get { return (string)GetValue(OneValueProperty); }
set { SetValue(OneValueProperty, value); }
}
public static readonly DependencyProperty OneValueProperty = DependencyProperty.Register(
"OneValue", typeof(string), typeof(SilverlightControl1), new PropertyMetadata(string.Empty));
public SilverlightControl1()
{
InitializeComponent();
this.DataContext = this;
}
}
SilverlightControl2.xaml
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="{Binding TwoValue}" Foreground="Blue" />
</Grid>
SilverlightControl2.xaml.cs
public partial class SilverlightControl2 : UserControl
{
public string TwoValue
{
get { return (string)GetValue(TwoValueProperty); }
set { SetValue(TwoValueProperty, value); }
}
public static readonly DependencyProperty TwoValueProperty = DependencyProperty.Register(
"TwoValue", typeof(string), typeof(SilverlightControl2), new PropertyMetadata(string.Empty));
public SilverlightControl2()
{
InitializeComponent();
this.DataContext = this;
}
}
As soon as you find yourself feeling the need to do this:-
this.DataContext = this;
know that you have probably got things wrong. Its probably the first thing I would expect to find on Silverlight specific "bad smell list".
In this case where you are specialising UserControl a better approach is to do this:-
SilverlightControl1.xaml
<Grid>
<Local:SilverlightControl2 x:Name="MyControl2" />
</Grid>
SilverlightControl1.xaml.cs (I'm just showing the constructor the rest is as you have it)
public SilverlightControl1()
{
InitializeComponent();
MyControl2.SetBinding(SilverlightControl2.TwoValueProperty , new Binding("OneValue") { Source = this });
}
SilverlightControl2.xaml
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="MyTextBox" Foreground="Blue" />
</Grid>
SilverlightControl1.xaml.cs (I'm just showing the constructor the rest is as you have it)
public SilverlightControl2()
{
InitializeComponent();
MyTextBox.SetBinding(TextBox.TextProperty , new Binding("TwoValue") { Source = this });
}
Since in UserControls you know the structure of the XAML and you can name the elements that you need access to in code, you can create the binding using a line of code instead.
This leaves the DataContext free to do what is designed for rather than be hi-jacked for a different purpose.
The alternative approach where instead of specialising UserControl you create a templated control, in this case the binding can be expressed in XAML using something like:-
<TextBox Text="{TemplateBinding TwoValue}" />
Template binding only works in ControlTemplate so you can't use it in a UserControl.
Assuming the following view model definition:
public class MyObject {
public string Name { get; set; }
}
public interface IMyViewModel {
ICommand MyCommand { get; }
IList<MyObject> MyList { get; }
}
And a UserControl with the following code behind:
public class MyView : UserControl {
public IMyViewModel Model { get; }
}
If my XAML looked like this:
<UserControl>
<ListBox ItemsSource="{Binding MyList}">
<ListBox.ItemTemplate>
<TextBlock Text="{Binding Name}" />
<Button Content="Execute My Command" cmd:Click.Command="{Binding Path=MyCommand, ?????????}" cmd:Click.CommandParameter="{Binding}" />
</ListBox.ItemTemplate>
</ListBox>
How can I bind my Button to the ICommand property of my code-behind class?
I'm using Prism and SL 3.0 and I need to bind each button in my list box to the same command on my view model.
Before my UserControl had a specific name and I was able to use the ElementName binding, but now my UserControl is used multiple times in the same parent view so I can't use that technique anymore and I can't figure out how to do this in XAML.
If it is my only option I can do it manually in the code-behind, but I'd rather do it declaratively in the XAML, if possible.
You need a DataContextProxy for this to work because you're no longer in the context of the UserControl. You've moved out of that and there is no good way to reach back into that context without something like the DataContextProxy. I've used it for my projects and it works great.