write string to textblock on different page in C#, UWP - c#

How can I write into a TextBlock on a different page?
So far it only works with TextBlocks on the same page.
The async function is in Page_1. The TextBlock is on Page_2.
public async void Serial()
{
string rxBuffer;
//code
//code
//code
while (true)
{
textblock_DebugRx_Gas_live.Text = rxBuffer;
}
}

write string to textblock on different page in C#, UWP
If the two page display in foreground at same time like following.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,20,0,0" HorizontalAlignment="Center">
<Button Click="Button_Click" Content="Click" />
<TextBlock x:Name="Tbk" />
</StackPanel>
<Frame Grid.Row="1" VerticalAlignment="Center">
<local:TestPage />
</Frame>
</Grid>
You could use Messenger to send message from original page to target page.
using GalaSoft.MvvmLight.Messaging;
private void Button_Click(object sender, RoutedEventArgs e)
{
var message = "Test";
Messenger.Default.Send<string,TestPage>(message);
Tbk.Text = message;
}
Target Page
public TestPage()
{
this.InitializeComponent();
this.Loaded += TestPage_Loaded;
}
private void TestPage_Loaded(object sender, RoutedEventArgs e)
{
Messenger.Default.Register<string>(this, (s) =>
{
MyTextBlock.Text = s;
});
}

Usually, pages are displayed one at a time, so if you want to pass data between them during navigation, you should do so using the second parameter of the Frame.Navigate method:
this.Frame.Navigate(typeof(SecondPage), someString);
However, an even better solution is to use some kind of MVVM framework, which has a navigation service based on ViewModels, which have a longer lifetime than the UI controls/page. Frameworks like MvvvmCross, MvvmLight, SimpleMvvm or Reactive UI can all help you write such logic in an easier manner.
In your case, you could store the updated state in a view model, which would then be shared by both pages, so any change would be reflected in both places. Instead of directly writing into the Text property of the TextBlocks, you would implement a view model with a string property, which would trigger PropertyChanged event from the INotifyPropertyChanged interface. The MVVM pattern is well described in many tutorials so I encourage you to dig into it.

Related

How can I capture WPF events from a Winforms app

I have an WPF User control which is is hosted in an Elementhost. I use elementhost to include an WPF user control in my classical Windows forms app.
Now, from Windows forms side I am trying to capture the mouseDown event that is produced in an WPF label but I don't know how to do it.
Any ideas?
A case might be able to help you. The winform form calls the wpf control.
Create a WPF custom control. The xaml code of the control is as follows.
<Grid>
<Image Margin="10,10,10,90" x:Name="img" Stretch="Uniform" Opacity="1">
<Image.BitmapEffect>
<DropShadowBitmapEffect Opacity="1" />
</Image.BitmapEffect>
</Image>
<TextBox Background="Transparent" Foreground="White" Height="40" FontSize="32" Margin="44,0,56,36" x:Name="txtBox1" Opacity="0.5" Text="" VerticalAlignment="Bottom" /> </Grid>
You need to add the corresponding function to set the effect. The code is as follows.
public void SetSource(string fileName)
{
img.Source = new BitmapImage(new Uri(fileName) );
}
public void SetOpacity(double opacity)
{
img.Opacity = opacity;
}
//
public string GetText()
{
return txtBox1.Text;
}
Create a Winform application and add a reference, otherwise the control will not work properly. The list of references is pictured below.
Regenerate the solution. On the left toolbar, a WPF control appears and drag it to the form.
Use the button control in the winform project to call the corresponding function.
private void button1_Click(object sender, EventArgs e)
{
((UserControl1)elementHost1.Child).SetSource(#"C:\Users\Admin\Pictures\Saved Pictures\9837f99502eba3d01d4fb671cab20c15.jpg");
}
private void button2_Click(object sender, EventArgs e)
{
((UserControl1)elementHost1.Child).SetOpacity(0.5);
}
private void button3_Click(object sender, EventArgs e)
{
string text = ((UserControl1)elementHost1.Child).GetText();
label1.Text = text;
}
Test items: The left side is the traditional Winform control. The right side is the imported WPF control. You can clearly see the "translucent" effect of the picture.
Not sure what exactly you're trying to achieve. Below is a simple example.You can edit the MouseDown event of the UserControl as needed.
If there is a problem, please make your problem clearer and show me the complete code sample that can reproduce your problem for analysis.
UserControl:
<Grid>
<Label x:Name="label" Content="Label" MouseDown="label_MouseDown" Background="AliceBlue" Width="300" Height="200" />
</Grid>
private void label_MouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("hello");
}
Add the UserControl reference in the WinForms project, drag and drop the UserControl on the Form1 designer after rebuilding the WinForms project.
The result of running the project and clicking the Label in the UserControl is shown in the figure.

WPF Databinding to second view

I have spend a little over a Day on this problem and i am absolutely Clueless.
If i click the button to show the Second View it Opens, but without Content.
I even get by a breakpoint in the View Model.
For this i have reduced everything to a Simple Textbox and Textblock that shut display the same Data, but they do not. They show nothing even after Typing into the Box the Block does not update.
But what ever i try the Databinding does not Work. Does anyone has an Idea?
Thanks in Advance
My second View
<Window x:Class="AoE4_BO_Overlay.Views.EditorView"
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:AoE4_BO_Overlay.Views" xmlns:viewmodels="clr-namespace:AoE4_BO_Overlay.ViewModels" d:DataContext="{d:DesignInstance Type=viewmodels:EditorViewModel}"
mc:Ignorable="d"
Title="EditorView" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="20" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Path=FirstName}" Grid.Column="0" Grid.Row="2"/>
<TextBox Text="{Binding Path=FirstName , Mode=OneWay}" Grid.Column="0" Grid.Row="1"/>
</Grid>
My ViewModel
internal class EditorViewModel : Conductor<object>
{
private string _firstName = "Tom";
public EditorViewModel()
{
}
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
NotifyOfPropertyChange(() => FirstName);
}
}
}
How i call both of them
public void CreateBO_Click(object sender, RoutedEventArgs e)
{
EditorView createBO = new EditorView();
ActivateItemAsync(new EditorViewModel());
createBO.Show();
}
added Information
public partial class EditorView : Window
{
public EditorView()
{
DataContext = new EditorViewModel();
InitializeComponent();
}
}
You have two separate issues:
1. Typing into the textbox doesn't change the bound property:
This is expected, since you use OneWay binding explicitly. OneWay binding means the property updates the user interface, but not the other way around. So changing FirstName should update the TextBox, but changing the TextBox doesn't update FirstName.
Interestingly enough, if you just omit the Mode = OneWay part, it should work - since TextBoxes should use TwoWay binding by default. I recommend you define your TextBox binding explicitly as Mode = TwoWay
2. Your view initializes with an empty TextBlock / TextBox
This one is harder to pin down, since you don't show us where you set your DataContext. This usually happens to me when I set the DataContext AFTER InitializeComponent(), instead of before. You either set the DataContext before the binding is initialized (as part of InizializeComponent()), or you have to raise a NotifyPropertyChanged on your property to update the UI afterwards.
If this is not the cause, you might want to enable WPF binding errors in your output console - that usually gives you a good idea of where your bindings fail. Visual Studio has an option for that. It should be located here:
Tools -> Options -> Debugging -> Output Window -> WPF Trace Settings
-> Data Binding -> All
I believe what you are attempting here is to show your second View (EditorView) within the first one (and not as a pop-up - if you intend to have it as popup, use WindowManager instead of ActivateItemAsync).
One thing you need to change for making this possible is to ensure your second View is a UserControl and not a Window.
// EditorView.xaml.cs
public partial class EditorView : UserControl
// EditView.xaml
<UserControl x:Class="AoE4_BO_Overlay.Views.EditorView"
Also since your using the ActivateItemAsync, you would need to ensure that your FirstView contains a ContendControl with Name "ActiveItem".
// FirstView.xaml
<ContentControl x:Name="ActiveItem"/>
The call to ActivateItemAsync would use this control to load the View of your second ViewModel (EditorViewModel). With this in place, you could now use the ActivateItemAsync method to load the View.
public async Task CreateBO_Click(object sender, RoutedEventArgs e)
{
await ActivateItemAsync(new EditorViewModel());
}
Please note that method ActivateItemAsync supports asynchronous calls and it would be wise to call the method asynchronously.
Another point to note is that you do not need to specify the DataContext explicitly as seen in the OP if you are using Caliburn Micro and the View/ViewModels are stored in the recommended folder/namespaces structures. Caliburn Micro uses naming conventions to associate the appropriate view-viewmodel pairs. More information on the same could be found in the official documentation

Trying to incorporate a timer in a game, with Visual Studio using C#

I am new to C# development, but I am trying to write a game for my children which incorporates a timer. I am using Microsoft Visual Studio Community 2015 (Version 14.0.25431.01 Update 3). I am using the "Blank" template, and I am targeting Universal Windows, "Windows 10 Anniversary Edition (10.0; Build 14393)".
On MainPage.xaml, I assumed I would be able to click on the Toolbox and select a "Timer" control, which would then appear in the code underneath, and which I would then be able to reference in MainPage.xaml.cs.
However, there is no "Timer" control available to select from the list of "All XAML Controls", and when I try to insert "using System.Timers" in MainPage.xaml.cs, it doesn't seem to recognise it.
All I need is for a timer to start "ticking" when the Start button is pressed, and to allow input for a fixed number of seconds before "timing out" and moving on to the next question.
Any pointers would be very gratefully received!
Sounds like you are used to programming in the drag and drop interface of WinForms or VB6... To accomplish what you are describing you would need a button and an input control in your XAML and then the timer would reside in code-behind (or if you were following MVVM then it would live in your view model). A simple implementation of this could look like:
MainPage.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" x:Name="StartButton" HorizontalAlignment="Stretch" Content="Start" Click="StartButton_Click" />
<TextBox Grid.Row="1" x:Name="InputField" HorizontalAlignment="Stretch" ></TextBox>
<TextBlock Grid.Row="2" x:Name="StatusText" HorizontalAlignment="Stretch"></TextBlock>
</Grid>
MainPage.xaml.cs
using System.Threading;
using Window.UI.Xaml;
using Window.UI.Xaml.Controls;
namespace UniversalTimerApp
{
public sealed partial class MainPage : Page
{
private Timer questionTimer;
public MainPage()
{
this.InitializeComponent();
this.questionTimer = new Timer(this.EndOfTimer, null, Timeout.Infinite, Timeout.Infinite);
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
this.questionTimer.Change(3000, Timeout.Infinite);
this.StatusText.Text = "Timer Started!";
}
private async void EndOfTimer(object state)
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
this.InputField.IsEnabled = false;
this.StatusText.Text = "Timer Ended.";
});
}
}
}

Trying to understand of DependencyProperty

Being new to WPF, and its apparently amazing ability to change, bind, enable, and otherwise manipulate. I'm trying to get a mental overview of what is happening and hope some can either confirm or correct my readings.
Before WPF, you have delegates and events. You could have a dozen controls all listening (via being registered to the event), so when the event fires, all other controls will be notified automatically and can act on however they were so coded. Such as...
From Code Behind, you would do something like
GotFocus += MyMethodToDoSomething;
Then, the signature method
private void MyMethodToDoSomething(object sender, RoutedEventArgs e)
{
.. do whatever
}
Additionally, by using standard getter / setter, the setter can call its own methods in its own class to do something every time someone tries to get or set a value
private int someValue;
public int SomeValue
{
get { this.DoSomeOtherThing();
return someValue;
}
set { this.DoAnotherThing();
someValue = value;
}
Now, there's dependency properties, and the one/two-way binding. I understand (I think) about one-way to simulate more of a read-only operation.
Anyhow, with two way binding, the dependencies automatically notify anyone "depending" on a change in either the source or target respectively, without an explicit check if something has subscribed to an event, the framework automatically handles the announcing of the change to the respective controls (target or source).
So, let me through this scenario out with an old Add/Edit Save/Cancel maintenance form.
In an older framework, if someone clicked on an add or edit button, all the data entry fields would become "enabled" with either blank data for a new record, or editing existing data. At the same time, the add/edit buttons would become disabled, but the Save/Cancel buttons would now become enabled.
Likewise when finished via Save/Cancel, it would disable all the entry fields, save/cancel, and re-enable the Add/Edit buttons.
I don't quite understand how such this type of scenario would be handled under this dependency property scenario (yet), but am I close? I also understand you can bind to almost anything, including color schemes, show/hide, fonts, etc... But I'm taking small steps on trying to really grasp this stuff.
Thanks.
The getter/setter stuff is a feature of regular C# properties. It isn't unique to WPF.
This one-way/two-way stuff is talking about WPF data binding, which doesn't require you to create Dependency Properties - just to use them.
Dependency properties are built into controls themselves. They let you directly reference those properties when adding instances of your control to the form. They allow your custom control to feel a bit more "native".
Generally they are used to implement a property that can use data binding. In your apps, you'll mostly just use data binding, rather than implement new hooks for it.
... if someone clicked on an add or edit button, all the data entry fields would become "enabled" with either blank data for a new record, or editing existing data. At the same time, the add/edit buttons would become disabled, but the Save/Cancel buttons would now become enabled.
Likewise when finished via Save/Cancel, it would disable all the entry fields, save/cancel, and re-enable the Add/Edit buttons.
I would accomplish what you want to accomplish with:
A view model
Data binding on the view to that view model
Exposing ICommand on that view model (for buttons)
INotifyPropertyChanged on the view model (for all properties)
No new dependency properties need to be created for this scenario. You'll just use existing ones to do data binding.
Here's a code sample/tutorial of doing WPF with data binding and MVVM style.
Setting up the project
I created a WPF application in the New Project wizard, and named it MyProject.
I set up my project name and namespaces to match the generally accepted scheme of things. You should set these properties in solution explorer -> project -> right click -> properties.
I also have a custom folder scheme I like to use for WPF projects:
I stuck the view in its own "View" folder for organizational purposes. This is also reflected in the namespace, since your namespaces should match your folders (namespace MyCompany.MyProject.View).
I also edited AssemblyInfo.cs, and cleaned up my assembly References and app config, but that is just some tedium that I'll leave as an exercise for the reader :)
Creating a view
Start off in the designer, and get everything looking nice. Don't add any code behind, or do any other work yet. Just play around in the designer until things look right (especially when you resize). Here's what I ended up with:
View/EntryView.xaml:
<Window x:Class="MyCompany.MyProject.View.EntryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Entry View" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Text="Test 1" Grid.Row="0" />
<TextBox Text="Test 2" Grid.Row="1" Margin="0,6,0,0" />
<TextBox Text="Test 3" Grid.Row="2" Margin="0,6,0,0" />
<TextBox Text="Test 4" Grid.Row="3" Margin="0,6,0,0" />
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Content="Edit" IsEnabled="True" Grid.Column="0"
HorizontalAlignment="Left" Width="75" />
<Button Content="Save" IsEnabled="False" Grid.Column="1"
Width="75" />
<Button Content="Cancel" IsEnabled="False" Grid.Column="2"
Width="75" Margin="6,0,0,0" />
</Grid>
</Grid>
</Window>
View/EntryView.xaml.cs:
using System.Windows;
namespace MyCompany.MyProject.View
{
public partial class EntryView : Window
{
public EntryView()
{
InitializeComponent();
}
}
}
I didn't create any Name properties on these controls. That is on purpose. I am going to use MVVM, and won't use any code behind. I'll let the designer do what it wants to do, but I won't touch any of that code.
Creating a view model
Next I will make my view model. This should be designed in a way that it services the view, but could ideally be view independent. I won't worry about that too much, but the point is you don't have to have a 1-to-1 parity of view controls and view model objects.
I try to make my views/view models make sense in a bigger app context, so I'll start purposing the view model here. We'll make this "editable form" a rolodex entry.
We'll create a helper class that we need first...
ViewModel/DelegateCommand.cs:
using System;
using System.Windows.Input;
namespace MyCompany.MyProject.ViewModel
{
public class DelegateCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public DelegateCommand(Action execute)
: this(execute, CanAlwaysExecute)
{
}
public DelegateCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
if (canExecute == null)
throw new ArgumentNullException("canExecute");
_execute = o => execute();
_canExecute = o => canExecute();
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
private static bool CanAlwaysExecute()
{
return true;
}
}
}
ViewModel/EntryViewModel.cs:
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace MyCompany.MyProject.ViewModel
{
public class EntryViewModel : INotifyPropertyChanged
{
private readonly string _initialName;
private readonly string _initialEmail;
private readonly string _initialPhoneNumber;
private readonly string _initialRelationship;
private string _name;
private string _email;
private string _phoneNumber;
private string _relationship;
private bool _isInEditMode;
private readonly DelegateCommand _makeEditableOrRevertCommand;
private readonly DelegateCommand _saveCommand;
private readonly DelegateCommand _cancelCommand;
public EntryViewModel(string initialNamename, string email,
string phoneNumber, string relationship)
{
_isInEditMode = false;
_name = _initialName = initialNamename;
_email = _initialEmail = email;
_phoneNumber = _initialPhoneNumber = phoneNumber;
_relationship = _initialRelationship = relationship;
MakeEditableOrRevertCommand = _makeEditableOrRevertCommand =
new DelegateCommand(MakeEditableOrRevert, CanEditOrRevert);
SaveCommand = _saveCommand =
new DelegateCommand(Save, CanSave);
CancelCommand = _cancelCommand =
new DelegateCommand(Cancel, CanCancel);
}
public string Name
{
get { return _name; }
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
public string Email
{
get { return _email; }
set
{
_email = value;
RaisePropertyChanged("Email");
}
}
public string PhoneNumber
{
get { return _phoneNumber; }
set
{
_phoneNumber = value;
RaisePropertyChanged("PhoneNumber");
}
}
public string Relationship
{
get { return _relationship; }
set
{
_relationship = value;
RaisePropertyChanged("Relationship");
}
}
public bool IsInEditMode
{
get { return _isInEditMode; }
private set
{
_isInEditMode = value;
RaisePropertyChanged("IsInEditMode");
RaisePropertyChanged("CurrentEditModeName");
_makeEditableOrRevertCommand.RaiseCanExecuteChanged();
_saveCommand.RaiseCanExecuteChanged();
_cancelCommand.RaiseCanExecuteChanged();
}
}
public string CurrentEditModeName
{
get { return IsInEditMode ? "Revert" : "Edit"; }
}
public ICommand MakeEditableOrRevertCommand { get; private set; }
public ICommand SaveCommand { get; private set; }
public ICommand CancelCommand { get; private set; }
private void MakeEditableOrRevert()
{
if (IsInEditMode)
{
// Revert
Name = _initialName;
Email = _initialEmail;
PhoneNumber = _initialPhoneNumber;
Relationship = _initialRelationship;
}
IsInEditMode = !IsInEditMode; // Toggle the setting
}
private bool CanEditOrRevert()
{
return true;
}
private void Save()
{
AssertEditMode(isInEditMode: true);
IsInEditMode = false;
// Todo: Save to file here, and trigger close...
}
private bool CanSave()
{
return IsInEditMode;
}
private void Cancel()
{
AssertEditMode(isInEditMode: true);
IsInEditMode = false;
// Todo: Trigger close form...
}
private bool CanCancel()
{
return IsInEditMode;
}
private void AssertEditMode(bool isInEditMode)
{
if (isInEditMode != IsInEditMode)
throw new InvalidOperationException();
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged Members
}
}
As is usual for this type of workflow, there are some requirements I missed when initially creating the view. For example, I figured out that it would make sense to have a "revert" feature that undoes the changes, but keeps the dialog open. I also figured out that I could reuse the edit button for this purpose. So I made a property that I will read to get the edit button's name.
The view model contains a lot of code to do something simple, but most of it is boilerplate for hooking up the properties. This boilerplate gives you some power, though. It helps isolate you from your view, so your view can change drastically with no changes or only minor changes to the view model.
If the view model gets too big, you can start pushing it into a additional sub view models. Create them wherever makes the most sense, and return them as properties on this view model. The WPF data binding mechanism supports chaining down the data context. You'll find out about this data context a little later when we hook things up.
Hooking up the view to our view model
To hook up the view to a view model, you have to set the DataContext property on view to point to your view model.
Some people like to instantiate and specify the view model in the XAML code. While this can work, I like to keep the view and the view model independent of each other, so I make sure I use some third class to hook the two up.
Normally I'd use a dependency injection container to hook up all my code, which is a lot of work, but keeps all the parts independent. But for an app this simple, I like to use the App class to bind my stuff together. Let's go edit it:
App.xaml:
<Application x:Class="MyCompany.MyProject.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="ApplicationStartup">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs:
using System.Windows;
namespace MyCompany.MyProject
{
public partial class App : Application
{
private void ApplicationStartup(object sender, StartupEventArgs e)
{
// Todo: Somehow load initial data...
var viewModel = new ViewModel.EntryViewModel(
"some name", "some email", "some phone number",
"some relationship"
);
var view = new View.EntryView()
{
DataContext = viewModel
};
view.Show();
}
}
}
You can now run your project, though the logic we built won't do anything. This is because our initial view is created, but it doesn't actually do any data binding.
Setting up data binding
Lets go back and edit the view to finish hooking it all up.
Editing View/EntryView.xaml:
<Window x:Class="MyCompany.MyProject.View.EntryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Rolodex Entry"
Height="350" Width="525"
MinWidth="300" MinHeight="200">
<Grid Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Name:" Grid.Column="0" Grid.Row="0" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1"
Grid.Row="0" Margin="6,0,0,0" />
<TextBlock Text="E-mail:" Grid.Column="0" Grid.Row="1"
Margin="0,6,0,0" />
<TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1"
Grid.Row="1" Margin="6,6,0,0" />
<TextBlock Text="Phone Number:" Grid.Column="0" Grid.Row="2"
Margin="0,6,0,0" />
<TextBox Text="{Binding PhoneNumber, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1" Grid.Row="2"
Margin="6,6,0,0" />
<TextBlock Text="Relationship:" Grid.Column="0" Grid.Row="3"
Margin="0,6,0,0" />
<TextBox Text="{Binding Relationship, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsInEditMode}" Grid.Column="1" Grid.Row="3"
Margin="6,6,0,0" />
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Content="{Binding CurrentEditModeName}"
Command="{Binding MakeEditableOrRevertCommand}"
Grid.Column="0" HorizontalAlignment="Left"
Width="75" />
<Button Content="Save" Command="{Binding SaveCommand}"
Grid.Column="1" Width="75" />
<Button Content="Cancel" Command="{Binding CancelCommand}"
Grid.Column="2" Width="75" Margin="6,0,0,0" />
</Grid>
</Grid>
</Window>
I did a lot of work here. First, the static stuff:
I changed the title of the form to match the Rolodex idea
I added labels for the fields, since I now know what they apply to
I changed the minimum width/height, since I noticed controls were getting cut off
Next the data-binding:
I bound all the text fields to the appropriate properties on the view model
I made the text fields update the view model on every keypress (UpdateSourceTrigger=PropertyChanged). This isn't necessary for this app, but could be helpful in the future. I added it to spare you from looking it up when you need it :)
I bound the IsEnabled field of each text box to the IsInEditMode property
I bound the buttons to their respective commands
I bound the edit button's name (Content property) to the corresponding property on the view model
Here's the result
Now all the UI logic works, except those we left a Todo comment on. I left those unimplemented because they have to do with a specific application architecture, and I didn't want to get into that for this demo.
Also, vanilla WPF doesn't have a very clean MVVM way to close a form that I know of. You can use code-behind to do it, or you can use one of the dozens of WPF add-on libraries that provide their own cleaner way of doing it.
Dependency Properties
You may have noticed that I didn't create a single custom Dependency Property in my code. The dependency properties I used were all on existing controls (e.g. Text, Content and Command). This is how it usually works in WPF, because data binding and styling (which I didn't get into) gives you a lot of options. It lets you completely customize the look, feel, and actions of built-in controls.
In previous Windows GUI frameworks, you'd often have to subclass existing controls or create custom controls to get a custom look and feel. The only reasons to make custom controls in WPF are to combine patterns of multiple controls in a reusable way, or to create a completely new control from scratch.
E.g. if you were making an auto-complete text box that is paired with a popup control to display the values it is auto-completing from. In such a case you might want to make a custom control, with custom dependency properties (such as the auto-completion source). That way you can reuse the control throughout your application, and other applications.
If you aren't making custom controls, or making special non-UI classes that can directly instantiate and use in XAML and data bind with, you probably won't need to create dependency properties.
The poster has requested that I repost my comment as an answer. Happy to oblige :-)
The video presentation I referred to: http://blog.lab49.com/archives/2650
Bonus link: awesome WPF article in MSDN: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
And in case you didn't know about it, there's a chapter in the online documentation: http://msdn.microsoft.com/en-us/library/ms752914.aspx
Also I've found this book very helpful: http://www.amazon.com/WPF-4-Unleashed-Adam-Nathan/dp/0672331195
My own experience with WPF involves going back between a bunch of different resources as I try to get my program to work. There's so much stuff in WPF it's really hard to keep it all in your head as you are learning it.
A simple way of looking at them is that they are properties that point to another property.
They're actually a definition of a property, that defines the property name, type, default value, etc, but the actual value of the property is not stored with the property definition.
So you can say a Button's Enabled property is going to point to a property on a specific class, or it it is going to point to CheckBoxA.IsChecked property, or you can even say it is simply going to be pointing to a boolean value of False.
// Value points to the current DataContext object's CanSaveObject property
<Button IsEnabled="{Binding CanSaveObject}" />
// Value points to the IsChecked property of CheckBoxA
<Button IsEnabled="{Binding ElementName=CheckBoxA, Path=IsChecked}" />
// Value points to the value False
<Button IsEnabled="False" />

How to keep WPF TextBox selection when not focused?

I want to show a selection in a WPF TextBox even when it's not in focus. How can I do this?
I have used this solution for a RichTextBox, but I assume it will also work for a standard text box. Basically, you need to handle the LostFocus event and mark it as handled.
protected void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
{
// When the RichTextBox loses focus the user can no longer see the selection.
// This is a hack to make the RichTextBox think it did not lose focus.
e.Handled = true;
}
The TextBox will not realize it lost the focus and will still show the highlighted selection.
I'm not using data binding in this case, so it may be possible that this will mess up the two way binding. You may have to force binding in your LostFocus event handler. Something like this:
Binding binding = BindingOperations.GetBinding(this, TextProperty);
if (binding.UpdateSourceTrigger == UpdateSourceTrigger.Default ||
binding.UpdateSourceTrigger == UpdateSourceTrigger.LostFocus)
{
BindingOperations.GetBindingExpression(this, TextProperty).UpdateSource();
}
Another option is to define a separate focus scope in XAML to maintain the selection in the first TextBox.
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="Text that does not loose selection."/>
<StackPanel Grid.Row="1" FocusManager.IsFocusScope="True">
<TextBox Text="Some more text here." />
<Button Content="Run" />
<Button Content="Review" />
</StackPanel>
</Grid>
TextBoxBase.IsInactiveSelectionHighlightEnabled Property has available since .NET Framework 4.5
public bool IsInactiveSelectionHighlightEnabled { get; set; }
public class CustomRichTextBox : RichTextBox
{
protected override void OnLostFocus(RoutedEventArgs e)
{
}
}
I found that the suggestions listed (add a LostFocus handler, defining a FocusScope) to not work, but I did come across the code listed here: http://naracea.com/2011/06/26/selection-highlight-and-focus-on-wpf-textbox/, which creates a custom Adorner that highlights the text when not focused.

Categories