I have a wpf application with a simple window that shows a text in a textbox bound to a property of the ViewModel. When I execute the application I can see the text in the textbox, but when I execute a white based test, I have different results, at random:
most of the times, it's not bound and the test fails.
sometimes it's bound (I can see the text in the window raised by white), but white throws an exception when the code wants to get the window.
sometimes (really few) it works.
The code I'm trying to execute is this:
<Window x:Class="Sample.Tests.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="22,19,0,0"
Name="name"
Text="{Binding Path=CountryName}"
VerticalAlignment="Top"
Width="120" />
</Grid>
</Window>
The code behind:
namespace Sample.Tests.Wpf
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
Loaded += delegate { DataContext = new MainWindowViewModel
{
CountryName = "Argentina"
};
};
}
}
}
and the test looks like:
var app = Application.Launch("Sample.Tests.Wpf.exe");
var window = app.GetWindow("MainWindow");
Assert.IsNotNull(window);
var textbox = window.Get<TextBox>("name");
Assert.AreEqual("Argentina", textbox.Text);
Any idea?
The Loaded event is not fired until the window is about to be rendered. My guess is that it has something to do with the timing of that event.
Does it work if you just assign the DataContext instead of waiting to respond?
namespace Sample.Tests.Wpf
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel
{
CountryName = "Argentina"
};
}
}
}
Related
I'm trying to learn ReactiveUI. Currently I'm playing with ReactiveUI.Validation.
I'm using a simple TextBox. Everything is working as expected if the binding doesn't have UpdateSourceTrigger=PropertyChanged. If i add it, it will hang when i type something in the textbox.
What do I miss?
Here is the MainWindow not working code:
<rxui:ReactiveWindow x:Class="ReactiveValidationTest.MainWindow"
x:TypeArguments="local:MainViewModel"
xmlns:rxui="http://reactiveui.net"
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"
xmlns:local="clr-namespace:ReactiveValidationTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Margin="50">
<TextBox x:Name="TxtBox"
Text="{Binding InputText,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch"/>
<TextBox HorizontalAlignment="Stretch"/>
</StackPanel>
</rxui:ReactiveWindow>
The code behind:
using ReactiveUI;
using ReactiveUI.Validation.Extensions;
using System.Reactive.Disposables;
namespace ReactiveValidationTest
{
public partial class MainWindow : ReactiveWindow<MainViewModel>
{
public MainWindow()
{
InitializeComponent();
ViewModel = new MainViewModel();
this.WhenActivated(d =>
{
this.WhenAnyValue(x => x.ViewModel)
.BindTo(this, x => x.DataContext)
.DisposeWith(d);
this.BindValidation(ViewModel, vm => vm.InputText, v => v.TxtBox.Text);
});
}
}
}
And the ViewModel:
using ReactiveUI.Fody.Helpers;
using ReactiveUI.Validation.Extensions;
using ReactiveUI.Validation.Helpers;
namespace ReactiveValidationTest
{
public class MainViewModel : ReactiveValidationObject
{
[Reactive]
public string InputText { get; set; }
//ctor
public MainViewModel()
{
this.ValidationRule(vm => vm.InputText,
txt => !string.IsNullOrEmpty(txt),
"Can not be null or Empty");
}
}
}
Thanks.
You have infinite loop, because you use this code:
this.BindValidation(ViewModel, vm => vm.InputText, v => v.TxtBox.Text);
Your current code reads input form control and set property, then ValidationRule is trying to update control, to show error message. It triggers update of input control, so again - it triggers ValidationRule. You use this mechanism wrong.
BindValidation method should be used to bind error message for given rule, which is connected with vm property to control which is designated to show that message.
You are trying to use build-in WPF mechanism which uses INotifyDataErrorInfo and your code is almost good. You should remove this.BindingValidation, because binding in XAML already do the job related with error message.
I have been doing development work in WPF application which uses an MVVM pattern for a couple of days now. I'm very new to WPF and MVVM pattern as well.
In my scenario, I have a user control view (named EPayView.xaml) which has a textbox that will accept a phone number. The view has a corresponding viewmodel (named EPayViewModel.cs). In the MainWindow.xaml, I have a user control (floating virtual keyboard) which is derived from namespace controls WpfKb.Controls. The MainWindow.xaml also has a corresponding viewmodel (named MainViewModel.cs)
Having said that, I have done research on how to use attached dependency properties which lead me to this solution. Set focus on textbox in WPF from view model (C#) which I believe this is where I could bind the property IsFocused in the textbox of EPayView.xaml.
Below are the codes that I have already incorporated in my solution.
EpayView.xaml (textbox xaml markup)
<TextBox Text="{Binding PhoneNo}" Grid.Row="5" Margin="10,0,10,0" VerticalContentAlignment="Center" FontSize="12" x:Name="Email" behaviors:FocusExtension.IsFocused="{Binding IsFocused, Mode=TwoWay}"/>
MainWindow.xaml (xaml markup)
<Window x:Class="SmartPole540.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WpfKb.Controls;assembly=SmartPole.WpfKb"
xmlns:wpf="clr-namespace:WebEye.Controls.Wpf;assembly=WebEye.Controls.Wpf.WebCameraControl"
xmlns:utilities="clr-namespace:SoltaLabs.Avalon.Core.Utilities;assembly=SoltaLabs.Avalon.Core"
xmlns:userControls="clr-namespace:SoltaLabs.Avalon.View.Core.UserControls;assembly=SoltaLabs.Avalon.View.Core"
xmlns:square="clr-namespace:SmartPole.View.Square;assembly=SmartPole.View"
xmlns:view="clr-namespace:SmartPole.View;assembly=SmartPole.View"
Title="CitiPulse"
WindowStartupLocation="Manual"
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
Name="mainWindow">
<userControls:RollPanel.BottomContent>
<square:SquareView Canvas.Top="1010" DataContext="{Binding DataContext.SquareViewModel,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type userControls:RollPanel}}}"/>
</userControls:RollPanel.BottomContent>
<controls:FloatingTouchScreenKeyboard
x:Name="floatKb" Width="500" Height="250" PlacementTarget="{Binding ElementName=MainGrid}"
Placement="Center" AreAnimationsEnabled="False" Visibility="Visible"
IsOpen="{Binding IsChecked, ElementName=kbButton}"/>
</Window>
In the above code, the user control RollPanel.BottomContent host the EPayView.xaml view inside another view which is RollPanel.xaml
EpayViewModel.cs contains the static class FocusExtension for the IsFocused attached property (refer to this solution - Set focus on textbox in WPF from view model (C#)). And, EPayViewModel.cs already implemented INotifyPropertyChanged which is wrapped inside a concrete class ObservableObject that accepts type of T. This is also same with MainViewModel.cs
public class EPayViewModel : ObservableObject<EPayViewModel>, IPaymentViewModel, IActiveViewModel
{ ... }
public class MainViewModel : ObservableObject<MainViewModel>
{ ... }
As such, my goal is that when the textbox in EPayView.xaml has the focus, the floating virtual keyboard (floatKb) in the MainWindow.xaml will be shown.
I'm stuck on how to proceed (I was thinking if a call to FocusExtension static class in EPayViewModel inside my MainViewModel.cs will suffice?), any help is greatly appreciated.
Cheers,
As AnjumSKhan already said, to react to some event in a MVVM way, you'll have to use Command. Command can be called within an EventTrigger, you will need to add a Reference to System.Windows.Interactvity component.
Let's assume you have a simple View and View Model and you need to show this View when the TextBox in a MainWindow got focus.
View (NewWindow.xaml)
<Window x:Class="My.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewWindow" Height="300" Width="300">
<TextBlock Text="{Binding Message}"/>
View Model
public class NewWindowViewModel
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; }
}
}
You also have a MainWindow, it is a main view for an app and it contains the target TextBox. You may see that there is an EventTrigger added to the TextBox and it has a property InvokeCommandAction which is binded to the MainWindowViewModel's command called ShowCommand.
Main Window
<Window x:Class="My.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" Title="MainWindow" Height="350" Width="525">
<TextBox Height="40" Text="{Binding Text}">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="GotFocus">
<Interactivity:InvokeCommandAction Command="{Binding ShowCommand}"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</TextBox>
In the Show method of MainWindowViewModel NewWindow view is created and got new NewWindowViewModel instance as a DataContext. RelayCommand class is presented in my answer to this question
MainWindowViewModel
public class MainWindowViewModel
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; }
}
private ICommand _increaseCommand;
public ICommand ShowCommand
{
get
{
if (_increaseCommand == null)
{
_increaseCommand = new RelayCommand(
p => true,
Show);
}
return _increaseCommand;
}
}
private void Show(object obj)
{
var w = new NewWindow();
var nvm = new NewWindowViewModel();
nvm.Message = "Test";
w.DataContext = nvm;
w.Show();
}
}
What is left is to create a new MainWindowViewModel and setup a DataContext for MainWindow.
public MainWindow()
{
InitializeComponent();
var mvm = new MainWindowViewModel();
mvm.Text = "Focus me!";
DataContext = mvm;
}
Hope it will help.
In XAML/WPF I have main window which contains Frame where I intend to put one of the user controls for given view of application.
<Window x:Class="MyApp.MainWindow"
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"
xmlns:ignore="http://www.galasoft.ch/ignore"
mc:Ignorable="d ignore"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Grid x:Name="LayoutRoot">
<Frame Source="Main/MainUserControl.xaml" Name="Main" />
</Grid>
</Window>
Now I want to navigate this Frame to other source inside MainUserControl:
<UserControl x:Class="MyApp.View.MainMenu.MainMenuUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:lex="http://wpflocalizeextension.codeplex.com"
xmlns:command="clr-namespace:MyApp.Command"
mc:Ignorable="d"
Style="{StaticResource Localizable}"
DataContext="{Binding MainMenu, Source={StaticResource Locator}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Button Content="{lex:Loc About}" FontSize="28" Grid.Row="1" Command="NavigationCommands.GoToPage" CommandParameter="/Menu/AboutUserControl.xaml" />
</Grid>
</UserControl>
But the navigation button About remains inactive during execution. I verified correctly that /Menu/AboutUserControl.xaml exists.
I'm obviously doing something wrong. How can I navigate owning window's frame from within user control? Preferably via XAML?
I assume you are using an MVVM framework. (I have added the critical elements here in case you aren't).
Your MainWindow.xaml should use an "ItemsControl" instead of a "Frame". A frame can work, but a better way is to use the ItemsControl like so:
<!-- Main Frame -->
<Grid Grid.Column="1" Margin="10" Name="MainWindowFrameContent">
<ItemsControl ItemsSource="{Binding Path=MainWindowFrameContent}" >
<!-- This controls the height automatically of the user control -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
In the constructor of my MainWindow.cs, I set the DataContext of the window to the MainViewModel:
using myProject.ViewModel;
public partial class MainWindow : Window
{
MainViewModel mMainViewModel;
public MainWindow()
{
InitializeComponent();
// Initialize MainViewModel and set data context to the VM
mMainViewModel = new MainViewModel();
DataContext = mMainViewModel;
}
}
(I'm not sure if this next part HAS TO be an observable collection, but I have implemented it as such and it seems to work well. The only downside is that I need to manually clear the ItemsControl before adding a new UserControl)
My MainViewModel implements the binding called "MainWindowFrameContent". All of my user controls are initialized within the MainViewModel.cs code. I have an additional ViewModel for each UserControl and assign the DataContext of the UserControl to the individual ViewModel before displaying the UserControl to the main window.
My MainViewModel.cs:
public class MainViewModel : ObservableObject
{
public MainViewModel()
{
}
// This handles adding framework (UI) elements to the main window frame
ObservableCollection<FrameworkElement> _MainWindowFrameContent = new ObservableCollection<FrameworkElement>();
public ObservableCollection<FrameworkElement> MainWindowFrameContent
{
get
{
return _MainWindowFrameContent;
}
set
{
_MainWindowFrameContent = value;
RaisePropertyChangedEvent("MainWindowFrameContent");
}
}
// This handles opening a generic user control on the main window
// The ICommand implementation allows me to bind the command of a button on the main window to displaying a specific page
public ICommand MainWindowDataEntryView
{
get
{
return new DelegateCommand(_MainWindowDataEntryView);
}
}
void _MainWindowDataEntryView(object obj)
{
DataEntryVM wDataEntryVM = new DataEntryVM();
DataEntryView wDataEntryView = new DataEntryView();
wDataEntryView.DataContext = wDataEntryVM;
MainWindowFrameContent.Clear();
MainWindowFrameContent.Add(wDataEntryView);
}
}
Then you need to make sure you have an ObservableObject.cs as part of your project:
using System.ComponentModel;
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And you need a DelegateCommand.cs class as part of your project:
using System;
using System.Windows.Input;
public class DelegateCommand : ICommand
{
private readonly Action<object> _action;
public DelegateCommand(Action<object> action)
{
_action = action;
}
public void Execute(object parameter)
{
_action(parameter);
}
public bool CanExecute(object parameter)
{
return true;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged { add { } remove { } }
#pragma warning restore 67
}
So, it's a bit of a lengthy explanation, but once you have the previous items set up, you can add a bunch of buttons to your MainWindow.xaml, bind each button to a command that adds a new UserControl to your ItemsControl. When your UserControl displays, you can add controls as you would like and use them.
I hope this helps!
I'm trying to get my head around WPF, Unity and MvvMlight (galasoft). So far my little set up works. If I run my application the label is filled with a random name generated by my DataService. (small victory getting all moving parts to work)
But in the design view of Visual Studio the label remains empty. How do i convince VisualStudio to render some 'design time' data in my label?
I'm using: Visual Studio Premium 2013, Unity 4.0.1, MvvmLight 5.2, .net 4.5
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IDataService, DataService>();
container.RegisterType<IMainViewModel, MainViewModel>();
MainWindow mainWindow = container.Resolve<MainWindow>();
mainWindow.Show();
base.OnStartup(e);
}
}
In App.xaml I have not defined the StartUpUri
MainWindow.xaml
<Window x:Class="UnityMvvmTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="500">
<Grid>
<Label x:Name="myLabel" Content="{Binding MyText}"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MainWindow(IMainViewModel theViewModel)
: this()
{
this.DataContext = theViewModel;
}
}
MainViewModel.cs
public class MainViewModel : ViewModelBase, IMainViewModel
{
private readonly IDataService _dataService;
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
if (IsInDesignMode)
{
// Code runs in design time data.
MyText = "Design Data";
}
else
{
// Code runs "for real"
MyText = _dataService.GetName();
}
}
public string MyText { get; set; }
}
I found a method, using hints from https://stackoverflow.com/a/3380895/249845
I created a second (flat) implementation of IMainVieModel in a separate namespace: UnityMvvmTest.ViewModel.Design. This implementation has no logic, it just fills the properties so the designer has some data to display.
This implementation is used in design time, since it is specified as the DesignTime DataContext. (with xmlns:d, xmlns:mc and xmlns:vm). The mc-namespace is needed to hide the d-namespace during runtime, see why.
The result is 5 extra lines in the Xaml, an extra (almost empty) implementation of IMainViewModel. And an extra (empty) constructor in code behind, instead of a constuctor that test for IsInDesignMode. This isn't a big deal, since unity will pick the constructor with the most parameters it can resolve.
MainWindow.xaml
<Window x:Class="UnityMvvmTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:UnityMvvmTest.ViewModel.Design"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True, Type=vm:MainViewModel}"
mc:Ignorable="d"
>
<Grid>
<Label x:Name="myLabel" Content="{Binding MyText}" />
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
// Contructor used in DesignTime
public MainWindow()
{
InitializeComponent();
}
//Constructor used by Unity
public MainWindow(IMainViewModel theViewModel)
: this()
{
this.DataContext = theViewModel;
}
}
MainViewModel.cs (design time implementation)
namespace UnityMvvmTest.ViewModel.Design
{
public class MainViewModel : IMainViewModel
{
public MainViewModel()
{
MyText = "my Design time data";
}
public string MyText { get; set; }
}
}
I'm new to WPF and i'm trying to learn the famous MVVM pattern,
I'm facing a small issue (i'm sure) when i try to bind simple command to some ViewModel
this is Simple UserControl i've created:
<UserControl x:Class="MVVM_UsingUserControl_Sample.View.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
>
<StackPanel DataContext="MyUserControlViewModel" Orientation="Vertical" >
<Button Margin="0,100,0,0" Height="50" Width="50" Content="PressMe" Command="{Binding Path=MyCommand}"/>
</StackPanel>
</UserControl>
and this is the User Control ViewModel
class MyUserControlViewModel : ViewModelBase
{
CommandBase m_MyCommand = null;
public MyUserControlViewModel()
{
}
public ICommand MyCommand
{
get
{
if (m_MyCommand == null)
{
m_MyCommand = new CommandBase(new Action<object>(DoSomething),
new Predicate<object>(CanDoSomething));
}
return m_MyCommand;
}
}
public void DoSomething(object i_Params)
{
MessageBox.Show("Inside MyUserControl DoSomething");
}
public bool CanDoSomething(object i_Params)
{
return true;
}
}
this is the Main window xaml (no code behaind)
Now the problem is :
My main window contains the userControl as is (inside stack panel) and nothing else.
i expect the command "MyCommad" will get invoke when i press the button "MyButton"
but it doesn't.
anyone has idea why ???
Big thanks.
In the constructor of your main window, set its DataContext to your ViewModel.
For example,
this.DataContext = new MyViewModel();
In your XAML, remove
DataContext="MyUserControlViewModel"
since the DataContext will inherit from the main window.
Everything should then work as you expect.