WPF ComboBox Undo Exception on Ctrl+Z (with Focus on SelectionChanged), Why? - wpf

Well, someone can tell me why this exception occurs in these conditions?
Test Window
<Window x:Class="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">
<StackPanel>
<ComboBox
Name="cmbTest"
IsEditable="True"
SelectionChanged="ComboBox_SelectionChanged">
</ComboBox>
</StackPanel>
</Window>
Code behind
Class MainWindow
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Dim source As New ObservableCollection(Of String)()
source.Add("AAA")
source.Add("BBB")
source.Add("CCC")
cmbTest.ItemsSource = source
End Sub
Private Sub ComboBox_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
Keyboard.Focus(cmbTest)
End Sub
End Class
Now, if you type AAA and then Ctrl+Z, ComboBox control raise this exception:
Cannot Undo or Redo while undo unit is open.
Someone can explain why?!? How it would be possible to avoid it?

"The Undo method does not work with the KeyPress or TextChanged events."
This has gotten me before.
Source: http://msdn.microsoft.com/en-us/library/system.windows.forms.textboxbase.undo.aspx

Related

How to prevent playing sound during Window is opening?

XAML
<Window x:Class="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>
<TabControl x:Name="TabControl1">
<TabItem Header="General"/>
<TabItem Header="Security" />
<TabItem Header="Details" />
</TabControl>
</Grid>
</Window>
vb.net
Class MainWindow
Private Sub TabControl1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles TabControl1.SelectionChanged
System.Media.SystemSounds.Asterisk.Play()
End Sub
End Class
I want to play sound when User clicks every TabItem.
The codes above play sound during Window is opening which I dont want.
So, how to prevent playing sound during Window is opening?
There is the property IsLoaded in Window, that can be used for that purpose.
You could check for the value of IsLoaded to determine whether to play the sound or not (it may well be that there are syntactic errors, my VB.NET is not that good ;) )
Class MainWindow
Private Sub TabControl1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles TabControl1.SelectionChanged
If IsLoaded Then
System.Media.SystemSounds.Asterisk.Play()
End If
End Sub
End Class
The first selection of the first tab should occur before Loaded is called, hence IsLoaded should be false and no sound should be played.

PopulatingTextBoxes with Items from ListBox

I have a ListBox1 and a series of TextBoxes from TextBox9 onwards.I can populate the ListBox1 with Items(in sequence).I am trying to transfer those items to TextBoxes in the same order(one item for one TextBox).
I tried this code:
Dim x As Integer = ListBox1.SelectedIndex
For x = 0 To 50
For Each ListBoxItem In ListBox1.Items
ListBox1.SelectedItem = "TextBox" & (x+9) & ".Text"
Next
Next x
The code compiles but nothing happens when i click the button.Can anyone help me out?
Thanks in advance
Venkatraman
One way among others could be using the FrameworkElement.FindName() method, see also the following sample.
FWIW, in WPF using the MVVM pattern makes much more sense than one might expect at first glance, where usually a ViewModel would provide UI content among other things, and you usually would not want to do too much in the View's code-behind.
Sample.XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" SizeToContent="WidthAndHeight"
Loaded="Window_Loaded">
<StackPanel x:Name="stackPanel">
<ListBox x:Name="l1"/>
<TextBox x:Name="t1"/>
<TextBox x:Name="t2"/>
<TextBox x:Name="t3"/>
<Button Content="Test" Height="25" Click="Button_Click"/>
</StackPanel>
</Window>
Sample.VB:
Class MainWindow
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
l1.Items.Add("Item1")
l1.Items.Add("Item2")
l1.Items.Add("Item3")
l1.Items.Add("Item4")
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim i As Integer
Dim s As String
For Each item As String In l1.Items
i = i + 1
s = String.Format("t{0}", i)
Dim control = Me.FindName(s)
If TypeOf control Is TextBox Then
control.Text = item
End If
Next
End Sub
End Class

How to make Dispatcher.BeginInvoke run in background in VB.net?

I need to run some part of application in background and allow user to update the UI while the sub is running in the background. I searched and I found out that in WPF I should use Dispatcher. Problem is even when I use dispatcher still my GUI is not usable till the all subs will finish. I attached a code here so you can have better perspective of what I mean.
For example in this code when a user run the application, system should run a new thread that will change the text of first textbox while use can update the text of the second textbox.
I am wondering if I am doing this right or not. Can anybody help me with this?
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:testDispacher"
Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<Grid>
<StackPanel>
<TextBlock>UCtextbox:</TextBlock>
<src:ToBeRunByDispacherUC x:Name="UC1" />
<TextBlock>Windowstxtbox:</TextBlock>
<TextBox x:Name="txtBox2" Width="100" Height="30"/>
</StackPanel>
</Grid>
Class Window1
Delegate Sub runSub()
Dim setTxt As runSub
Public Sub New()
InitializeComponent()
setTxt = AddressOf UC1.setTxtBox
End Sub
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
UC1.IsEnabled = False
Dispatcher.Invoke(setTxt, Windows.Threading.DispatcherPriority.Background)
End Sub
End Class
<UserControl x:Class="ToBeRunByDispacherUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBox x:Name="txtBox1" Width="100" Height="30"/>
</Grid>
Partial Public Class ToBeRunByDispacherUC
Public Sub setTxtBox()
Dim j As Integer = 0
For i As Integer = 0 To 10
j += 1
System.Threading.Thread.Sleep(1000)
Next
txtBox1.Text = "End"
Me.IsEnabled = True
End Sub
End Class
The dispatcher should be used to update UI objects from a separate thread, it does not actually spawn up the thread for you. If you are using .NET 4.0 or higher, you can use the TPL library to spawn your thread, do your work, then update your UI object via the dispatcher from the background thread.
Task.Factory.StartNew(Sub() DoBackgroundWork())
Then, inside DoBackgroundWork whenever you want to update your UI...
Dispatcher.BeginInvoke(Sub() txtBox1.Text = "End")
you can call to the general application.
Application.Current.Dispatcher.Invoke()

DesignTime only error when trying to load an xml to a dataset

I've created a simple WPFApplication project that produce this error
This is the exact error:
Could not find file
'C:\Users\Andrei\AppData\Local\Microsoft\VisualStudio\11.0\Designer\ShadowCache\dj2szrx3.5nm\e412xqna.uj1\Locality.xml'. E:\BugDemo2\BugDemo2\MainWindow.xaml 7 9 BugDemo2
MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" x:Class="MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<!--This is the line that Visual Studio says it produce the error -->
<local:MainWindowViewModel x:Key="MainWindowViewModelDataSource" d:IsDataSource="True"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource MainWindowViewModelDataSource}}"/>
</Window>
MainWindowViewModel.vb
Imports System.Data
Imports System.IO
Public Class MainWindowViewModel
Shared ds As New DataSet("Locality")
Public Sub New()
MySub()
End Sub
Sub MySub()
'this is the line that actually gives the error
ds.ReadXml(
Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly.Location) &
"\Locality.xml")
MsgBox(ds.Tables.Count)
End Sub
End Class
I have nothing in the code behind MainWindow.xaml.vb
Class MainWindow
End Class
The program does exactly what it is supposed to do, it loads Locality.xml to dataset correctly and shows how many tables are in it.
If I move the ds variable in another module, or in another class, as a shared property I get
Object reference not set to an instance of an object
also as a Designtime only error.
I understand that Visual Studio doesn't know if Locality.xml, but why does it care and how do I make this error go away?
Use protective code, and more debuggable since you can check the variable before executing ReadXml:
Sub MySub()
Dim path As String = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly.Location) &
"\Locality.xml"
If IO.File.Exists(path) Then
ds.ReadXml(path)
MsgBox(ds.Tables.Count)
End If
End Sub

Showing/Closing forms in wpf

Im developing an application using wpf template I have these 2 windows:
MainWindow.xaml and
JungleTimer.vb which is a Windows Form
I have a button in my main windows which shows JungleTimer form using this code:
Dim JungleTimer As New JungleTimer
JungleTimer.Show()
But as you see, clicking this button multiple times will show multiple JungleTime form.
I tried to use this code to check if JungleTimer is visible but it doesn't work:
Dim JungleTimer As New JungleTimer
If JungleTimer.Visible = False Then
JungleTimer.Show()
End If
I also need the code to close the JungleTimer form.
As you are creating a new JungleTimer each time you click the button you will always get a new instance of the window. What you need to do is declare a field within the class of the type JungleTimer. Initially this will be null (Nothing). When you click the button, check if this field has a value or is still null. If still null, set it to a new JungleTimer and show it. If it isn't null, activate the existing window without creating a new instance. you'll also need to detect when the window closes so that you can set the field back to null.
For a demo, create a new WPF application with two windows, MainWindow (the main window) and JungleTimer.
XAML for MainWindow:
<Window x:Class="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">
<StackPanel VerticalAlignment="Center">
<Button Width="100" Height="30" Click="Jungle_Click">Jungle Me</Button>
<Button Width="100" Height="30" Click="DeJungle_Click">De-Jungle Me</Button>
</StackPanel>
VB for MainWindow (sorry if it's clumsy, I haven't done VB for ten years or so):
Class MainWindow
Private WithEvents _jungleTimer As JungleTimer
Private Sub Jungle_Click(sender As Object, e As RoutedEventArgs)
If _jungleTimer Is Nothing Then
_jungleTimer = New JungleTimer
_jungleTimer.Show()
Else
_jungleTimer.Activate()
End If
End Sub
Private Sub DeJungle_Click(sender As Object, e As RoutedEventArgs)
If Not _jungleTimer Is Nothing Then
_jungleTimer.Hide()
_jungleTimer = Nothing
End If
End Sub
Private Sub CloseHandler() Handles _jungleTimer.Closed
_jungleTimer = Nothing
End Sub
End Class
XAML for JungleWindow:
<Window x:Class="JungleTimer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="JungleTimer" Height="300" Width="300">
<Grid>
<Label HorizontalAlignment="Center" VerticalAlignment="Center">
Jungle!
</Label>
</Grid>

Resources