WPF Newbie from Forms: Basic Events understanding - c#

Even though late to the party, our company is finally moving slowly but surely to WPF for our desktop programming needs.
I have a few questions I was hoping this community could help me with... b/c I am finding even though I am reading all the tutorials in the world, that with WPF, there are so many different ways to approach the most basic things you used to do with Forms, that I just need a 101...
For instance...
Can anyone please enlighten a frustrated WPF newbie why this BASIC code is not working?
private void BtnImport_Click(object sender, RoutedEventArgs e)
{
//Button disabled on it's own without below routines
BtnImport.IsEnabled = false;
// So does textbox which updates on it's own without below routines
TxtTest.Text = "Started at : " + DateTime.Now.ToString() + "\n";
//Bunch of routines that each run in their own loops
}
When I try to disable the button and update the textbox along with those routines mentioned.. the routines run JUST FINE.. .but the button NEVER gets disabled nor does the textbox get updated?
I have a feeling I still am not understanding how WPF does things in Bindings, but I was hoping for a light bulb moment from here to just point me out WHY the basics don't work here?
Thanks~
Thank you so much. here is the XAML. #Joe I totally get that I am not using bindings, and I think that is a CORE of my confusion, if I am setting it directly, why is it not working when other routines are running after I directly set a property? (and my novice thought was that it was bindings). In any case, as requested here is XAML code.
<Window x:Class="WpfApplication1.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" Background="#FF8FB1B1" Name="AnalyticsWindow" Loaded="AnalyticsWindow_Loaded">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="74*" />
<ColumnDefinition Width="429*" />
</Grid.ColumnDefinitions>
<TextBox Height="23" Margin="66,62,187,0" Name="TxtTime" VerticalAlignment="Top" Grid.ColumnSpan="2"/>
<Button Content="Import" Height="23" HorizontalAlignment="Left" Margin="141,179,0,0" Name="BtnImport" VerticalAlignment="Top" Width="75" Click="BtnImport_Click" Grid.Column="1" />
<DatePicker Height="25" HorizontalAlignment="Left" Margin="64,108,0,0" Name="ObjDateFrom" VerticalAlignment="Top" Width="115" Grid.Column="1" />
<DatePicker Height="25" HorizontalAlignment="Left" Margin="218,108,0,0" Name="ObjDateTo" VerticalAlignment="Top" Width="115" Grid.Column="1" />
<Label Content="Log" Height="28" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Margin="234,28,39,0" Name="label2" VerticalAlignment="Top" Width="156" Grid.Column="1" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="218,62,0,0" Name="TxtTest" VerticalAlignment="Top" Width="182" Grid.Column="1" />
</Grid>

Based upon what you wrote, the code is actually working, but not giving the UI 'enough time to breath' so that your changes can be rendered on the user surface. Consider changing to this...
Dispatcher.BeginInvoke((Action) (() => {
BtnImport.IsEnabled = false;
TxtTest.Text = "Started at : " + DateTime.Now.ToString() + "\n";
}));
with a symmetrical BeginInvoke at the end of your intensive processing so that the button is enabled again. This will mitigate your problem and help you get the behaviour you are after.
I would also suggest that the parts stated as...
//Bunch of routines that each run in their own loops
...are candidates for the WPF BackgroundWorker or passing the code off to a Task<> that you can await upon. Doing this will keep the UI responsive and let you enable/disable buttons and update text blocks in 'real-time'.
The BackgroundWorker or Task<> is the so-called 'golden solution' if the UI thread is totally starved out. There are a zillion samples out there you can easily cut and paste into your code. I recommend the Task<> approach because it is not reliant upon WPF namespaces.
In its simplest form, you can do this...
Task.Run(() =>
{
// lots of business processing code here
});
And synch up your stuff using await...

Related

UI not updating using MVVM toolkit in WinUI 3

I am trying to change the size of a grid using data binding in order to be able to expand certain sections of my UI. I have looked online and nothing really seems to work. My window has a frame that loads a "root page" with another frame inside of it as seen in rootpage.xaml below. I have added a textbox but that does not seem to work. Interestingly enough, when I click the button present on the page. I do get a line outputted so my code is running. Adding a breakpoint shows similar results that the property FullScreenValue does in fact change. I think I am missing something in order to get the PropertyChanged event to be received or I may be unintentionally instancing multiple instances of my ViewModel. Looking for a solution.
My root page loaded into the frame in my window:
<Page
x:Class="MVVM.Views.rootPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVM.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ViewModels="using:MVVM.ViewModels"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<ViewModels:RootPageViewModel x:Name="ViewModel"/>
</Page.DataContext>
<Grid x:Name="frameGrid" ColumnDefinitions="*, *" RowDefinitions ="*, *" Grid.Row="1" Margin="15, 0, 15, 0" >
<Frame x:Name="topleftFrame" Grid.Column="0" Grid.Row="0" CornerRadius="8" Margin="10, 10, 10, 10" Grid.ColumnSpan="{Binding FullScreenValue,Mode=OneWay}" Grid.RowSpan="{Binding FullScreenValue, Mode=OneWay}"/>
<AppBarButton Icon="FullScreen" Grid.Column="0" Grid.Row="0" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,5,5,10" Command="{Binding FullScreenCommand, Mode=OneWay}"/>
Visibility="Visible"/>
<TextBlock Text="{Binding FullScreenValue, Mode=OneWay}" Grid.Row="1" Grid.Column="1"/>
</Grid>
</Page>
My ViewModel:
namespace MVVM.ViewModels
{
public partial class RootPageViewModel : ObservableObject
{
[ObservableProperty]
private int fullScreenValue;
public RootPageViewModel()
{
fullScreenValue = 1;
}
[ICommand]
void FullScreen()
{
fullScreenValue = 2;
Debug.WriteLine("HI");
}
}
}
As you can see I am trying to make use of the community toolkit. I have tried replacing some of these generated snippets with standard mvvm properties and commands to no avail. If I change the constructor to have a value of 2 I get my desired result, but can't seem to do it with this button! Send help.
My desired result is upon clicking the button, the frame will now span 2 columns and 2 rows instead of 1 column and 1 row. Maybe because this sort of UI concerns strictly the view it does not necessarily need a ViewModel but nonetheless I am still looking for help as I feel like I will run into this problem in the future. I am new to a lot of things in the Microsoft ecosystem so please forgive me if this is a repeat question and I just could not understand other answers.

Element is already the child of another element when doesnt exist?

I'm trying to programatically create a button flyout, within my XAML I have:
<Page.Resources>
<Button x:Key="LaunchFlyout" Content="LAUNCH">
<Button.Flyout>
<Flyout Placement="Top">
<Grid Width="200" Height="200">
<StackPanel>
<Rectangle Fill="Red" Width="100" Height="100" />
<Rectangle Fill="Green" Width="100" Height="100" />
</StackPanel>
</Grid>
</Flyout>
</Button.Flyout>
</Button>
</Page.Resources>
Nested within grids I have:
<Grid x:Name="launchBtn_grid" Grid.Column="1">
</Grid>
And then in my code within the Page_Loaded method I have:
bool hasContainer = localSettings.Containers.ContainsKey("appStatus");
if (!hasContainer) {
Button button = (Button)this.Resources["LaunchFlyout"];
launchBtn_grid.Children.Add(button);
}
else {
Button button = new Button();
button.Content = "LAUNCH";
button.Click += launch_btn_Click;
launchBtn_grid.Children.Add(button);
}
When I debug this, it reaches the IF statement and reaches this line launchBtn_grid.Children.Add(button); and then I get this error Element is already the child of another element.
Does anyone understand why? I have already looked and they dont already exist so I don't understand why it is giving me this error. Does anyone know what I am doing wrong?
I'm not sure in what context/use case your are doing that, but it feels weird to me to have an actual control as a Resource (not a DataTemplate, Style, etc).
If you only want to have 1 button of the 2 different template, why not switch Visibility on the 2 instead of loading controls from your code behind ?
Going forward with the idea, just add both buttons in the Grid within your XAML and switch their Visibility according to the setting you read.
There is a BooleanToVisibilityConverter within the framework to help you with this.

Save text box data to a local file in a Windows 8 app

I'm learning how to build Windows 8 apps in XAML and I'm somewhat new to it. I have three text boxes and a button on a page and I want to take the text in each text box and save it to a file locally.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<TextBox HorizontalAlignment="Left" Margin="321,160,0,0" TextWrapping="Wrap" Text="First Name" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Margin="321,211,0,0" TextWrapping="Wrap" Text="Last Name" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Margin="321,260,0,0" TextWrapping="Wrap" Text="Age" VerticalAlignment="Top"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="321,324,0,0" VerticalAlignment="Top"/>
</Grid>
It doesn't really matter what kind of file it is, I'm going to assume a .txt file is fairly easy to create. I've tried to wire up a FileStream on the button click, but intellisense wouldn't allow me to add the necessary usings for it. I'm starting to get the feeling that there is another way you're supposed to handle binary files in Windows 8 apps. If someone could guide me into the right documentation or quickly show me how to set it up, I'd appreciate it.
A fairly easy to use method is File.WriteAllLines as shown in this topic
All you have to do now is retrieve the value from the textboxes. Make sure you assign them a Name property, so you can target them in your code-behind:
<TextBox Name="ageTextBox" HorizontalAlignment="Left" Margin="321,260,0,0" TextWrapping="Wrap" Text="Age" VerticalAlignment="Top"/>
Then in your Code-Behind, say on the button click, add the Text to a list:
List<string> myList = new List<string>();
myList.Add(ageTextBox.Text);
Finally, you write the contents of your list to a specified file:
string path = "D:\\Temp\\MyFile.txt";
File.WriteAllLines(path, myList);
Do note you have to create the eventhandler yourself, name the other textboxes and add their texts to the list too.

How do always display the Numeric keyboard in windows phone 8?

I'm working on windows phone 8 app, I had a page which inputs number for that I gave code like this,
<TextBox Name="txtNumber" Height="Auto" Margin="0,10,0,510" >
<TextBox.InputScope>
<InputScope>
<InputScopeName NameValue="Number" />
</InputScope>
</TextBox.InputScope>
</TextBox>
by the above code; It display the numeric keyboard when I place the cursor to type; But I need a fixed keyboard which is always visible and if we type it has to enter the value to the textbox.
Would somebody please tell me how to do that.
Try this on for size:
Xaml:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0"
Loaded="ContentPanel_Loaded">
<TextBox
Name="TB1"
HorizontalAlignment="Left"
Height="72"
Margin="0,74,0,0"
VerticalAlignment="Top"
Width="456"
InputScope="Number"/>
</Grid>
Code:
private void ContentPanel_Loaded(object sender, RoutedEventArgs e)
{
// Turn on Tab Stops. You can set this in XAML as well.
this.IsTabStop = true;
// Set focus on the TextBox.
TB1.Focus();
}
It will spark up the SIP as it enters the <TextBox> ready for input. Hope it's what your looking for.
Got it from this MSDN blog.
The easiest way would probably be with creating your own user control. However it is most likely a lot of work to get it to work as a normal keyboard.
http://www.silverlightshow.net/items/Creating-a-Silverlight-Custom-Control-The-Basics.aspx
Or maybe perhaps this will help http://www.silverlightshow.net/items/Windows-Phone-7-Creating-Custom-Keyboard.aspx

c# WPF how to produce a flashy warning

I'm learning WPF, so bear with me.
I would like to have my WPF application flash in the user's face if a certain event is fired.
What is the best way to "notify" the user? I really want the user to react!
Cheers, Patrick
Environment: Windows7/64bit/.Net4
If you want the user to react you can force them to by simply opening a modal dialogue. The most lightweight of which being the MessageBox. You can also create normal modal windows using their ShowDialog method, you can make those windows as "fancy" as you want by getting rid of their normal appearance. This is achieved by setting the WindowStyle to None and AllowsTransparency to true, this will remove all the frame elements, so the window is now pure content.
Popups are handy for non-modal notifications and they already are content-only, but setting their AllowsTransparency to true may also be desired if you want rounded corners for example.
Best is entirely subjective and depends on many context variables but here is how I do it MVVM style.
In your main view model, define a property
pubic ObservableCollection<AlertViewModel"> Alerts { get; private set; }
in my case the AlertViewModel has only a "Message" property and a "Dismiss" RelayCommand.
In the XAML of your main view add
<Grid>
<all of my other other view controls>
<ItemsControl x:Name="AlertsControl" Opacity="50" ItemsSource="{Binding Alerts}"/>
</Grid>
Make sure it is the last item in the main container of your main view. This ensures it has the highest z order and will appear on top of all other controls.
Here is the data template for this view model
<DataTemplate DataType="{x:Type vm:AlertViewModel}">
<Border CornerRadius="10" Margin="3" Background="Red">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Margin="10" Grid.Column="0"
Command="{Binding ElementName=theWindow, Path=DataContext.DismissAlarmCommand}"
CommandParameter="{Binding}">Dismiss</Button>
<TextBlock Foreground="White" FontWeight="ExtraBold" Grid.Column="1"
Text="{Binding Message}" FontSize="20"
VerticalAlignment="Center" HorizontalAlignment="Left"></TextBlock>
</Grid>
</Border>
</DataTemplate>
Now,
Alerts.Add( new AlertViewModel() { Message = "Danger Will Robinson! Danger!" } );
Will pop a Bright red alert box onto the top of your main form. It does not go away until the user presses "Dismiss"
If you want it to flash or fade in and out or bounce up and down you can add animation in the data template.
You can use a Converter or data to Enable/Disable the rest of the controls in the app byt binding to AlertsControl.HasItems
Good luck.

Categories

Resources