I put several ComboBoxes on a XAML window. When I expand any of them, the DropDown part appears on the upper left corner of the screen.
I use Visual Studio 2008 C# Express. I don't remember this phenomenon when I used Visual Studio 2008 (Trial Version), though I use the same FrameWork (3.5).
It seems to be a bug.
Workaround:
Use Window.Show() instead with a custom logic to simulate the ShowDialog() behavior.
This appears to be a bug in WPF. In my case, I was trying to open a window in the Loaded event of another window. To get around this, I set a timer up to fire, then used a delegate to open the window (cannot open the window in a timer event because the calling thread that opens a window must be STA).
Edit - timer isn't necessary - didn't see the answer above just queue it on the dispatcher...
private delegate void DelegateOpenWindow();
private DelegateOpenWindow m_DelegateOpenWindow;
private Timer loginTimer = new Timer(200);
private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
{
// create delegate used for asynchronous call
m_DelegateOpenWindow= new DelegateOpenWindow(this.OpenWindow);
// start a timer to fire off the open window.
loginTimer.Elapsed += loginTimer_Elapsed;
loginTimer.Enabled = true;
}
void loginTimer_Elapsed(object sender, ElapsedEventArgs e)
{
loginTimer.Enabled = false;
this.Dispatcher.BeginInvoke(m_DelegateOpenWindow);
}
void OpenWindow()
{
MyWindow w = new MyWindow();
w.Owner = this;
w.ShowDialog();
}
I started observing this (and other strange behavioral quirks) yesterday when I tried to "tweak" window sizes, shapes, colors, and invoke a log-on dialog from the Window.Loaded event handler. I had been doing this just fine in each of a dozen+ individual "MVVM" pattern apps. Yesterday, I decided to move this from each app's code behind into a consolidated code-behind base class, since the pre-processing had become common in all those apps. When I did, the drop-downs in two ComboBoxes in the log-in dialog suddenly appeared in the upper left corner of my screen. I seem to have "solved" it by using the following technique (your mileage may vary):
protected void WindowBaseLoadedHandler(object sender, RoutedEventArgs e)
{
...non-essential lines of code removed...
if (DataContext != null)
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
/*----------------------------------------------------------------------
* Do we have a View Model? If so, perform standard VM Initialization...
*---------------------------------------------------------------------*/
this.IsEnabled = false;
LoginDlg loginDlg = new LoginDlg();
loginDlg.ShowDialog();
if (!loginDlg.Success)
{
/*-----------------------------------
* Log on failed -- terminate app...
*----------------------------------*/
...termination logic removed...
}
this.IsEnabled = true;
}));
}
WindowBaseLoadedHandler is the Loaded event handler. LoginDlg is a WPF app with a dialog containing two ComboBoxes.
Recap: After I consolidated the code into the Loaded event handler of the base class the ComboBox's drop down lists appeared in the upper left corner of my screen. Once I wrapped the logic into the Dispatcher.BeginInvoke call, the appropriate ComboBox behavior returned with lists below the current item.
I suspect WPF needs the application to return from the Loaded event to complete the layout system's initialization. That doesn't fully explain why it worked before, but I'll have to queue up my desire to hunt that "why" down for some rainy day in the future and celebrate overcoming the latest obstacle for today.
In any event, I hope someone finds this of use.
I'm using the latest .Net 4.5 and WPF framework and I still have this problem. One thing I noticed is that it only happen when there's an attached debugger. When the debugger is not attached, everything works fine.
I had the same problem on Visual Studio 2019.
Using window.Show() can help but it can ruin your design.
The solution is to open the window asynchronously.
var yourDialog= new YourDialog();
yourDialog.Owner = this;
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
this.Dispatcher.BeginInvoke(new Action(() =>
completion.SetResult(yourDialog.ShowDialog())));
bool? result = await completion.Task;
You can also create a more elegant solution by making the extension method:
public static class AsyncWindowExtension
{
public static Task<bool?> ShowDialogAsync(this Window self)
{
if (self == null) throw new ArgumentNullException("self");
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
self.Dispatcher.BeginInvoke(new Action(() => completion.SetResult(self.ShowDialog())));
return completion.Task;
}
}
And you can use it like this:
await dlgReview.ShowDialogAsync();
It’s a bug in WPF (not the only one, I'm afraid). It happened when I opened another window in the Loaded Event, something like:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Window selectionWindow = new SelectionWindow();
bool? result = selectionWindow.ShowDialog();
if (result == true)
RecordChanged();
}
I already found a workabout.
Related
I have a background worker process that starts provisioning a new client for our system. Here is what the DoWork method looks like:
ProvisioningManager manager = new ProvisioningManager(false)
{
};
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.MaxSteps = manager.MaxProgress;
}));
manager.StatusUpdated += new ProvisioningManager.StatusUpdatedHandler(manager_StatusUpdated);
manager.TaskCompleted += new ProvisioningManager.TaskCompleteHandler(manager_TaskCompleted);
manager.ProvisionClient();
while (!manager.Completed)
{
System.Threading.Thread.Sleep(100 * 60);
}
Basically it creates the manager that handles talking to the different sub-systems which provision the client.
Now I have a status update event and completed event for the provisioning manager. When the TaskCompleted event fires I want to be able to set a property on my display object so that the finish button in the wizard is enabled:
void manager_TaskCompleted(object sender, ProvisioningManager.Task taskType)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.ProvisioningComplete = true;
}));
}
The XAML for the button looks like this:
<wizard:WizardPage Header="Provisioning Client..."
ShowBack="False"
AllowBack="False"
AllowFinish="{Binding Source={StaticResource ResourceKey=dataObject}, Path=ProvisioningComplete}"
Loaded="Provisioning_Loaded">
</wizard:WizardPage>
This isn't working. Even though I make sure to hit the dispatcher thread to set the property of the display object it doesn't actually change the button to enabled until I click on the window. Is this a bug in AvalonWizard or am I not on the correct thread to set an INotifyPropertyChanged? Is there a way to hack this; basically can I programmatically focus the window without the mouse click?
I tired placing that while loop in the DoWork method so that I could use the BackgroundWorker's completed method:
void provisioningWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.ProvisioningComplete = true;
}));
}
That doesn't work either. What gives?!
Update
Here is the requested static resource instantiation for the display object:
<Window.Resources>
<ObjectDataProvider x:Key="dataObject" ObjectType="{x:Type winDO:NewClientWizardDO}" />
</Window.Resources>
Update II
Here is the property and property change firer:
public bool ProvisioningComplete
{
get { return this._ProvisioningComplete; }
set
{
this._ProvisioningComplete = value;
this.NotifyPropertyChanged("ProvisioningComplete");
}
}
protected void NotifyPropertyChanged(params string[] propertyNames)
{
if (this.PropertyChanged != null)
{
foreach (string propertyName in propertyNames)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
sorry if I don't understand something, but is the ProvisioningComplete property marked as "volatile"? If not then this might be the problem.
So I couldn't find out exactly why I was having this issue. I tried setting focus to the window, the button, etc. I tried multiple ways of letting the view know the viewmodel had updated. Basically every suggestion I could find on the web didn't work. It almost seems like a bug.
A smarty on my team suggested faking a mouse click on the window. His idea was that since all it took to activate the button was a simple mouse click on the screen then faking one should have the same effect. I thought (and think) that this hack was ridiculous. I did try it out just to see if I could call it a "solution".
Well, it worked. We had this same problem in another one of our wizards (not AvalonWizard but a homegrown one). I think there has to be some underlying issue with the way the window redraws after a background thread updates objects that are bound to the UI.
Anyhow, the way I found to solve this issue is with the following hack-tastic code.
//import user32.dll and setup the use of the mouse_event method
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
/// <summary>
/// Watches for properties to change on the data object, mainly the ProvisioningComplete method
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void DataObject_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "ProvisioningComplete":
//if the provisioning is completed then we need to make the finish button selectable.
if (this.DataObject.ProvisioningComplete)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
//give the window focus
this.Focus();
//update the layout
WizardPageProvisioningClient.UpdateLayout();
//fake mouse click 50 pixels into the window
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (uint)(this.Left + 50), (uint)(this.Top + 50), 0, 0);
}));
}
break;
}
}
I've tested this when the window is not the active window and when the user leaves the window as selected. The focus method seems to take care of this issue when the window isn't active. Our QA team hasn't run a complete test against the UI so I can't say if there is any situations where it doesn't work, but it seems to be the best solution that I've come up with to date.
I'm open to any other suggestions if anyone out there has a better idea of what could be causing the button to not update.
I've got a WPF usercontrol in a winforms application form.
Basically, I want a generic eventhandler attached to my WPF TreeView to handle "Document.NodeChanged". As this particular event fires when the tree is populated, I attempted to do a late attachment, via my treeview control's Loaded event.
The code goes something like:
private void UpdateGrid()
{
myGridView.UpdateXML(entityId, runDate, rtbToggleFullView.ToggleState == Telerik.WinControls.Enumerations.ToggleState.Off, userName);
//Safely attach the event to fire when the treeview has finished loading.
myGridView.tvRatings.Loaded -= AttachNodeChangedEvent;
myGridView.tvRatings.Loaded += AttachNodeChangedEvent;
}
Then the "AttachNodeChangedEvent" method looks like this:
public void AttachNodeChangedEvent(object i, EventArgs a)
{
((XmlDataProvider)myGridView.dataProvider).Document.NodeChanged -= OnNodeChanged;
((XmlDataProvider)myGridView.dataProvider).Document.NodeChanged += OnNodeChanged;
}
With a simple OnNodeChanged method:
public void OnNodeChanged(object i, EventArgs a)
{
Dirty = true;
}
The idea being:-
UpdateGrid runs UpdatesXML on treeview
Attaches a "NodeChangeHandler attacher" to treeview.Loaded
(when treeview is loaded) treeview fires "NodeChangeHandler attacher" which then attaches "OnNodeChanged" to the Treeview's populated XmlDocumentProvider.
This appeared to work perfectly in Windows 7. In Windows XP, however, the AttachNodeChangedEvent routine fires, and experiences a NullReferenceException (presumably because the Document hasn't loaded yet?) crashing the app.
Commenting out the ...((XmlDataProvider)myGridView... lines fixes the crash, but obviously disables the functionality.
Can anyone suggest a better way of achieving the same, or shed some light on why this works for Windows 7, but not Windows XP? "e.g. Attach NodeChangedEvent after the initial population of the Treeview"
I can confirm that both use the appropriate .Net Framework 4 package and seem to have all other dependencies appropriately included.
Thanks!
I use a browse for files dialog to allow a user to select multiple images. If a lot of images are selected, as expected it takes a bit. Below is an example of what I do with the selected images. I loop through the filepaths to images and create an instance of a user control, the user control has an Image control and a few other controls. I create the instance of this control then add it to a existing stackPanel created in the associating window xaml file. The example just below works fine, but I'm trying to understand BackGroundWorker better, I get the basics of how to set it up, with it's events, and pass back a value that could update a progress bar, but because my loop that takes up time below adds the usercontrol instance to an existing stackPanel, It won't work, being in a different thread. Is BackGroundWorker something that would work for an example like this? If so, what's the best way to update the ui (my stackpanel) that is outside the thread. I'm fairly new to wpf and have never used the BackGroundWorker besides testing having it just update progress with a int value, so I hope this question makes sense, if I'm way off target just let me know. Thanks for any thoughts.
Example of how I'm doing it now, which does work fine.
protected void myMethod(string[] fileNames) {
MyUserControl uc;
foreach (String imagePath in fileNames) {
uc = new MyUserControl();
uc.setImage(imagePath);
stackPanel.Children.Add(uc);
progressBar.Value = ++counter;
progressBar.Refresh();
}
}
below this class i have this so I can have the progressBar refresh:
public static class extensionRefresh {
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement) {
uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
}
}
Check out this article on
Building more responsive apps with the Dispatcher
Now that you have a sense of how the Dispatcher works, you might be surprised to know that you will not find use for it in most cases. In Windows Forms 2.0, Microsoft introduced a class for non-UI thread handling to simplify the development model for user interface developers. This class is called the BackgroundWorker
In WPF, this model is extended with a DispatcherSynchronizationContext class. By using BackgroundWorker, the Dispatcher is being employed automatically to invoke cross-thread method calls. The good news is that since you are probably already familiar with this common pattern, you can continue using BackgroundWorker in your new WPF projects
Basically the approach is
BackgroundWorker _backgroundWorker = new BackgroundWorker();
// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);
// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do something
}
// Completed Method
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Doing UI stuff
if (e.Cancelled)
{
statusText.Text = "Cancelled";
}
else if (e.Error != null)
{
statusText.Text = "Exception Thrown";
}
else
{
statusText.Text = "Completed";
}
}
Using a BackgroundWorker alone won't solve your issue since elements created during the DoWork portion will still have originated from a non-UI thread. You must call Freeze on any objects you intend to use on another thread. However only certain UI objects will be freezable. You may have to load in the images as BitmapImages on the background thread, then create the rest of your user control on the UI thread. This may still accomplish your goals, since loading in the image is probably the most heavyweight operation.
Just remember to set BitmapImage.CacheOption to OnLoad, so it actually loads up the image when you create the object rather than waiting until it needs to be displayed.
I have some RoutedCommands for commands like control-A, copy paste and they all work fine.
Then I added 4 more routedcommands to move object up down left and right in the canvas using arrowkeys, they sometimes works and sometime doesn't. At first I thought it was a Focus issue on the Canvas but I just found out that at the same time, all the other routedcommands like control-A works but arrowkeys doesn't.
I really have no idea what's going on here, they are identical routedcommands with different variable names, how come one works 100% of time and one only work 50% of time?
Working RoutedCommand:
_bindings.Add(new CommandBinding(DesignerCanvas.SelectAll, SelectAll_Executed));
SelectAll.InputGestures.Add(new KeyGesture(Key.A, ModifierKeys.Control));
private void SelectAll_Executed(object sender, ExecutedRoutedEventArgs e)
{
SelectionService.SelectAll();
}
Malfunctioning RoutedCommand:
_bindings.Add(new CommandBinding(DesignerCanvas.MoveDown, MoveDown_Executed));
MoveDown.InputGestures.Add(new KeyGesture(Key.Down));
private void MoveDown_Executed(object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
select item;
if (selectedItems.Count() > 0)
{
for (int i = 0; i < selectedItems.Count(); i++)
selectedItems.ElementAt(i).Top += Option.OptionSingleton.Sensitivity;
}
}
The malfunctioning RoutedCommand is just not firing sometimes, especially after I open some other window and come back to the canvas, then it will stop firing while other routedcommands are unaffected. Any ideas what's causing this weird behavior?
You can sometiems use very inclusive class event handlers to trace the route of an event:
EventManager.RegisterClassHandler(typeof(FrameworkElement), CommandManager.CanExecuteEvent,
new CanExecuteRoutedEventHandler((s, e) => Debug.WriteLine("CanExecute: " + s)), true);
EventManager.RegisterClassHandler(typeof(FrameworkElement), CommandManager.ExecutedEvent,
new CanExecuteRoutedEventHandler((s, e) => Debug.WriteLine("Executed:" + s)), true);
EventManager.RegisterClassHandler(typeof(FrameworkElement), CommandManager.ExecutedEvent,
new CanExecuteRoutedEventHandler((s, e) => Debug.WriteLine("KeyDown:" + s)), true);
In your case the KeyDown may be handled before it reaches the command binding or the CanExecute event may not reach it for some other reason.
Hopefully this will help you debug the problem
This may be due to the fact that the key you are using is the "Down" key. I suspect that if you used a different key, it would work.
Some controls consume the arrow keys and pageup/pagedown keys. For example, TextBox does this. If your Canvas is in a scrollviewer, the scrollviewer might be eating it.
There are two workarounds for this:
Add a binding to the control that is eating the key gesture.
Handle KeyPreview for the Canvas (or any parent of the control that is eating the keystroke) and execute the command from there.
The answer to this question shows how you can do #2 without writing specific code in the KeyPreview handler for each command.
It turns out that it was a focus issue, I just set the focus to the canvas whenever mouse enters, now it's sort of fixed. Thanks everybody for answering.
I know that there are several implementations here and there, but i was still not able to 'lock' on something really useful...
Whenever i set some component DataContext or ItemsSource to some big object, there is this 'render time frozen GUI' which make the app real annoying (even when using Virtualization).
I know i can iterate the object and set the items one by one and show progress, but i am looking for some other approach which can let me show some moving indication while GUI is rendering. I also prefer to have some progress bar and not only make the mouse cursor change.
Is there a decent way to achieve the followings?
Many Thanks
Zamboni example is a very good one, but still does not solve the frozen GUI problem.
As mentioned, there is no currently simple way of having something 'alive' to update a gui control while GUI is busy rendering.
I currently found some event that is 'alive and kicking' while gui is rendering, althogh it should be turned off when not needed as it can fire something like 60 times per second.
CompositionTarget.Rendering += ReportRenderProgress;
You can then implement ReportRenderProgress() anyway you like to signal you progress bar to update. Currently, i dont see any better solution available in WPF to update a progress indication while rendering so i am marking this as the answer.
This is actually a problem. You are using the GUI thread to fill the data (from object structure into GUI). The GUI thread is required both to read Windows message queue (prevent app from freezing, allow app to be moved/respond) and it is required to do any updates to the GUI.
One solution could be to slowly fill the the object structure after binding. This would have to be done from the GUI thread, so you could add DoEvents() and/or some percent indicator+forced refresh to make application seem alive.
I am interested to hear if anyone has a better solution though.
BackgroundWorker has everything you need.
EDIT
In WPF the Dispatcher is being employed automatically to invoke cross-thread method calls.
Check out Build More Responsive Apps With The Dispatcher in MSDN magazine.
I also put together some code fragments from a ViewModel that shows a BackgroundWorker updating a progress bar.
<ProgressBar
VerticalContentAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Minimum="0" Maximum="100"
Value="{Binding Path=BarPosition, Mode=TwoWay}"/>
// configure the background worker...
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
_backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);
// control progress bar position
private int _barPosition = 0;
public int BarPosition
{
get { return _barPosition; }
set
{
_barPosition = value;
OnPropertyChanged("BarPosition");
}
}
// long operation
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
if (bw != null)
{
int pos;
for (int i = 0; i < 100; ++i
{
// report progress here for our long running operation..
pos = i/100;
bw.ReportProgress(pos);
Thread.Sleep(1000); // fake long operation
}
}
}
// report progress,,,
void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
if (bw != null)
{
BarPosition = e.ProgressPercentage;
}
}
// reset scroll bar position
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
if (bw != null)
{
BarPosition = 0;
// Forcing the CommandManager to raise the RequerySuggested event to refresh UI...
CommandManager.InvalidateRequerySuggested();
}
}