WPF InvalidOperationException Getting MainWindow Reference - wpf

I have MainWindow, which contains an instance of DemoUI (a UserControl).
From within a class instance called DemoModule, I have a reference to DemoUI which I call _demoUI.
When I try to get a reference to the MainWindow from within DemoModule using
var parentWindow = Window.GetWindow(_demoUI);
I get this InvalidOperationException:
The calling thread cannot access this object because a different thread owns it.
Ultimately, I want to be able to update the MainWindow's progress bar's value using it's Dispatcher as follows:
var progressBar = parentWindow.FindName("ProgressBar") as ProgressBar;
progressBar.Dispatcher.Invoke(DispatcherPriority.Normal,
new DispatcherOperationCallback(o => {
progressBar.Value = Progress = args.Current;
return null;
}), null);
Update 1
public void OnProgressChanged(object sender, ProgressChangedEventArgs args)
{
Progress = Convert.ToInt32(args.Current * 100);
var progressBar = Application.Current.MainWindow.FindName("ProgressBar") as ProgressBar;
if (progressBar != null)
progressBar.Value = Progress;
}

From the code you include here it doesn't seem like you need to get the progressBar reference in order to update the ProgressBar on your _demoUI? Is there another ProgressBar on the MainWindow? Regardless, you can use the _demoUI reference to get to the dispatcher.
_demoUI.Dispatcher.Invoke(DispatcherPriority.Normal,
new DispatcherOperationCallback(o => {
_demoUI.ProgressBar.Value = Progress = args.Current;
return null;
}), null);
Or
_demoUI.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(o => {
var window = Window.GetWindow(_demoUI);
//do what you need to with the window here.
}), null);

Related

How can I bring the main WPF window to the front?

How do maximize and bring a WPF mainwindow to the front of my desktop? I have a filewatcher monitoring a directory. If a new file is created in the directory I want to bring my WPF apps main window to the front of the dekstop. As you can see I have tried several methods on the mainwidnow.
Modified Code: (I am getting the following error: "The calling thread cannot access this object because a different thread owns it")
DispatcherOperation o = Dispatcher.CurrentDispatcher.BeginInvoke(new
Action(delegate
{
var win = System.Windows.Application.Current.MainWindow;
win.Activate();
win.WindowState = System.Windows.WindowState.Normal;
win.Topmost = true;
win.Focus();
}), System.Windows.Threading.DispatcherPriority.ContextIdle, null);
Debug.WriteLine("Invoke");
o.Wait();
Modified 2 (Tried getting the dispatcher of the main window. I still get " "The calling thread cannot access this object because a different thread owns it".
System.Windows.Application.Current.MainWindow.Dispatcher.Invoke(() =>
{
var win = System.Windows.Application.Current.MainWindow;
win.Activate();
win.WindowState = System.Windows.WindowState.Normal;
win.Topmost = true;
win.Focus();
}, DispatcherPriority.Normal);
UPDATE (Working)
I guess I was not calling the dispatcher tied to the mainwindow with the above examples. I ended up creating a variable called _mainWindow of type MainWindow in the window class. In the MainWindow constructor I instantiated the variable:
_mainwidow = this;
Then I pass the _mainwindow variable in the constructor of the class where I use the FileWatcher. Here I can access the dispatcher of the _mainwindow variable:
_mainWindow.Dispatcher.Invoke(() =>
{
var win = System.Windows.Application.Current.MainWindow;
win.Activate();
win.WindowState = System.Windows.WindowState.Normal;
win.Topmost = true;
win.Focus();
}, DispatcherPriority.Normal);
Try this code
Dispatcher.Invoke(() =>
{
this.Activate();
this.WindowState = System.Windows.WindowState.Normal;
this.Topmost = true;
this.Focus();
});

asynchronous UI update from ViewModel in WPF

I am having a problem with getting data from db and showing in UI asynchronously.
I am using MVVM light, when I click the button, action is triggered in ViewModel:
private void SearchQuery(string query)
{
_redisModel.GetFriendsListAsync(query);
}
At some point GetFriendsListCompleted is called by background thread notifing viewmodel that job is done.
At this point I need to update ListBox ItemSource. But when I try to update is I get
“The calling thread cannot access this object because a different thread owns it”
I have tried Dispatcher.CurrentDispatcher.Invoke(),App.Current.Dispatcher.Invoke() and different magic, but it still doesn’t work.
I tried to give UI dispatcher to ViewModel and then call it from there - didn't work.
private string filterText = string.Empty;
public string FilterText
{
get { return filterText; }
set
{
filterText = value;
this.RaisePropertyChanged(() => this.FilterText);
this.FriendsList.View.Refresh(); // Here where exception is happening.
}
}
I tried to change this line to
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
() =>this.FriendsList.View.Refresh())); - still the same.
I am using Telerik ListBox to display items. FriendList is CollectionViewSource(http://www.telerik.com/help/wpf/radlistbox-overview.html). It works when I use Telerik example from WPF Control Examples. Problems start to occur when I use my async methods.
Type of view is System.ComponentModel.ICollectionView it is used for Filtering and Grouping.
I have also tried to just assign ObservableCollection to Items property of the ListBox and it doesn't work either.
A bit more details on how _redisModel.GetFriendsListAsync works:
In the end(after all chain of calls) it ends up here:
public GetAsyncResult(Func<T> workToBeDone, Action<IAsyncResult> cbMethod, Object state)
{
_cbMethod = cbMethod;
_state = state;
QueueWorkOnThreadPool(workToBeDone);
}
ThreadPool.QueueUserWorkItem(state =>
{
try
{
_result = workToBeDone();
}
catch (Exception ex)
{
_exception = ex;
}
finally
{
UpdateStatusToComplete(); //1 and 2
NotifyCallbackWhenAvailable(); //3 callback invocation
}
});
In viewmodel I have method:
private void GetFriendsListCompleted(object sender, ResultsArgs<Friend> e)
{
if (!e.HasError)
{
var curr = e.Results;
if (curr != null)
{
this.FriendsList= new CollectionViewSource();
this.FriendsList.Source = list;
this.FriendsList.Filter += this.FriendFilter;
FilterText = "";
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
() => this.FriendsList.View.Refresh()));
}
}
Can anybody please help me with this ?
Thank you
You are creating CollectionViewSource in one thread and refreshing that in another thread (dispatcher thread). Update your GetFriendsListCompleted to
private void GetFriendsListCompleted(object sender, ResultsArgs<Friend> e)
{
if (!e.HasError)
{
var curr = e.Results;
if (curr != null)
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
() => {
this.FriendsList= new CollectionViewSource();
this.FriendsList.Source = list;
this.FriendsList.Filter += this.FriendFilter;
FilterText = "";
this.FriendsList.View.Refresh();
}));
}
}
}
You haven't shown any of the code that's actually running on the background thread on completion but I'm guessing that in it you're creating a collection object that you're then trying to assign to your CollectionView. When the CV tries to update (on the UI thread) from your Refresh call it would then try to use the collection that's owned by the other thread.
If you include the relevant code it would be easier to say for sure.

How to Cancel windowManager.ShowDialog() from ViewModel

I have a ShellViewModel which loads a Modal Dialog. The Dialog's ViewModel has its OnActivate() override, where it gathers the data to be displayed on the Dialog. I would like to know how can we ask the WindowManager to cancel its ShowDialog based on a condition in OnActivate of the ViewModel backing the dialog.
For example, lets say that I have following code in ShellViewModel which tries to load a modal dialog based on StationOpenViewModel
public class ShellViewModel : Conductor<object>, IShell, IHandle<ConnectionChangedEvent> {
public void ShowOpenStationPage() {
StationOpenViewModel viewModel = container.GetExportedValue<StationOpenViewModel>();
windowManager.ShowDialog(viewModel);
}
...
}
and here is to code of OnActivate override of the StationOpenViewModel
public class StationOpenViewModel : Screen {
...
protected override void OnActivate() {
try {
using (StationRepository stationRepository = new StationRepository()) {
//code to get Station Data
}
catch (Exception ex) {
//Here I have no data, so there is no point in showing the window.
//How to cancel showDialog() for this viewModel
}
...
}
So in the above code, if I get Exception in OnActivate override, I don't have any Station data to show and I would like to cancel the showDialog() for the StationOpenViewModel. I tried using TryClose(), but if I do so, the WindowManager.ShowDialog() throws exception saying that the operation is invalid.
In summary, if I call WindowManager.ShowDialog() for a dialog backed by some ViewModel, then in that ViewModel how do I cancel the ShowDialog() operation.
The ShowDialog() implementation in CM source is:
public virtual void ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null)
{
var view = EnsureWindow(rootModel, ViewLocator.LocateForModel(rootModel, null, context));
ViewModelBinder.Bind(rootModel, view, context);
var haveDisplayName = rootModel as IHaveDisplayName;
if(haveDisplayName != null && !ConventionManager.HasBinding(view, ChildWindow.TitleProperty)) {
var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay };
view.SetBinding(ChildWindow.TitleProperty, binding);
}
ApplySettings(view, settings);
new WindowConductor(rootModel, view);
view.Show();
}
full source here:
http://caliburnmicro.codeplex.com/SourceControl/changeset/view/ae25b519bf1e46a506c85395f04aaffb654c0a08#src/Caliburn.Micro.Silverlight/WindowManager.cs
It doesn't look like there is a good way to do this with the default implementation. You should probably implement your own WindowManager and subclass the original implementation
The WindowConductor in the above code file is responsible for the lifecycle of the window, therefore and additional interface which your VMs can implement would work well:
public interface ICancelActivate
{
public bool ActivationCancelled { get };
}
Then just change your MyWindowConductor implementation to something like:
public MyWindowConductor(object model, ChildWindow view)
{
// Added this field so the window manager can query the state of activation (or use a prop if you like)
public bool ActivationCancelled;
this.model = model;
this.view = view;
var activatable = model as IActivate;
if (activatable != null)
{
activatable.Activate();
}
// Added code here, check to see if the activation was cancelled:
var cancelActivate = model as ICancelActivate;
if(cancelActivate != null)
{
ActivationCancelled = cancelActivate.ActivationCancelled;
if(ActivationCancelled) return; // Don't bother handling the rest of activation logic if cancelled
}
var deactivatable = model as IDeactivate;
if (deactivatable != null) {
view.Closed += Closed;
deactivatable.Deactivated += Deactivated;
}
var guard = model as IGuardClose;
if (guard != null) {
view.Closing += Closing;
}
}
then to stop the view from showing:
// This is in 'ShowDialog' - you can override the default impl. as the method is marked virtual
ApplySettings(view, settings);
// Get a ref to the conductor so you can check if activation was cancelled
var conductor = new MyWindowConductor(rootModel, view);
// Check and don't show if we don't need to
if(!conductor.ActivationCancelled)
view.Show();
Obviously I've just thrown this together so it might not be the best way, and I'd look carefully at where this leaves the state of your application
Your VMs just implement this:
public class StationOpenViewModel : Screen, ICancelActivation {
private bool _activationCancelled;
public bool ActivationCancelled { get { return _activationCancelled; } }
...
protected override void OnActivate() {
try {
using (StationRepository stationRepository = new StationRepository()) {
//code to get Station Data
}
catch (Exception ex) {
_activationCancelled = true;
}
...
}
... of course there may be better ways for you to check if you need to open a VM in the first place - I'm not sure what they would be but still, worth thinking about
Edit:
The reason I didn't just do this in the WindowManager...
new WindowConductor(rootModel, view);
var cancel = rootModel as ICancelActivation;
if(cancel == null || !cancel.ActivationCancelled) // fixed the bug here!
view.Show();
Is twofold - 1: you are still letting the WindowConductor add Deactivate and GuardClose hooks even though they should never be used, which may lead to some undesirable behaviour (not sure about reference holding either - probably ok with this once since nothing holds a ref to the conductor/VM)
2: it seems like the WindowConductor which activates the VM should be responsible for handling the cancellation of activation - ok it does mean that the WindowManager needs to know whether to show the VM or not, but it seemed a more natural fit to me
Edit 2:
One idea might be to move view.Show() into the conductor - that way you can cancel the activation without needing to expose details to the manager. Both are dependent on each other though so it's the same either way to me

set ItemContainerStyle from code

I am working on a custom wpf control which is derived from a ListBox and am trying to apply some formatting to a custom property.
When a particular custom property is false, I want to apply some formatting to the ListBox.
I am using the following code to attempt to apply the styling -
var t = new Trigger();
var BackgroundSetter = new Setter {Property = BackgroundProperty, Value = null};
var BrushSetter = new Setter { Property = BorderBrushProperty, Value = null };
t.Setters.Add(BackgroundSetter);
t.Setters.Add(BrushSetter);
var s = new Style(typeof(ListBox));
s.Triggers.Add(t);
editor.ItemContainerStyle.Triggers.Add(t);
I have also tried the following with no luck -
editor.ItemContainerStyle = s;
I am getting an error that indicates that some object was not initialized and stepping through shows that editor.ItemContainerStyle is null.
The actual error message just says Exception has been thrown by the target of an invocation.
Does anyone have any idea what I might be doing wrong?
Thanks
I was able to get this working - below is the code that I actually ended up using -
public bool IsSelectable
{
get { return (bool)GetValue(IsSelectableProperty); }
set { SetValue(IsSelectableProperty, value); }
}
public static DependencyProperty IsSelectableProperty = DependencyProperty.Register("IsSelectable", typeof(bool), typeof(ListEditor), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(IsSelectablePropertyChanged)) { BindsTwoWayByDefault = true });
private static void IsSelectablePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var editor = sender as ListEditor;
var s = new Style(typeof(ListBoxItem));
var enableSetter = new Setter {Property = IsEnabledProperty, Value = editor.IsSelectable};
s.Setters.Add(enableSetter);
editor.ItemContainerStyle = s;
}

wpf threading, thread must be STA

I have a window(say main window) with a frame which has a page in it. A button on the page opens another window(say popup window). Now i am trying to invoke a method in the main window from a button on the popup window. The method has to be multi-threaded, i had a similar solution running in windows forms but i keep getting the calling thread must be STA because many UI components require this in WPF.
The method on the page which opens the popup window modally
Scripts showStocks = new Scripts();
showStocks.ShowInTaskbar = false;
showStocks.ShowDialog();
if (showStocks.DialogResult==true)
{
Window1 wd1 = new Window1();
wd1.doneDeal();
}
Here window1 is our main window. The doneDeal method is
public void doneDeal()
{
// **Some Code**
BackgroundWorker wworks1 = new BackgroundWorker();
wworks1.DoWork += Tickes;
wworks1.RunWorkerCompleted += Tickes2;
wworks1.RunWorkerAsync();
// Page1 pg1 = frame1.Content as Page1;
//NextPrimeDelegate dd=new NextPrimeDelegate(okreport);
// pg1.addScriptBtn.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
// new NextPrimeDelegate(okreport));
//startStopButton.Dispatcher.BeginInvoke(
// DispatcherPriority.Normal,
// new NextPrimeDelegate(CheckNextNumber));
//new Thread(() => Tick(stock, rowID, exchange)) { IsBackground = false }.Start();
}
Finally the method that i am trying to run in the background
public void Tickes(object sender, DoWorkEventArgs e)
{
}
Also i want to populate a gridview from the result of the tickes method, this will be looping and running over and over in the background but periodically returning data to be added to the grid. SHould i do that in the progress update event ? Have tried a lot to wrap my head around the dispatcher and background worker in wpf but am failing to understand the STA apartment state bit. If someone can help me to get my tickes method going or point me in the right direction, i would be very very thankful.
Well i finally was able to solve the STA thread problem am posting the answer just in case somebody comes across a similar problem in the future.
public void doneDeal()
{
if (StockData.flag == 1)
{
row1 = table.NewRow();
row1[col1] = "";
row1[col2] = "";
row1[col3] = "";
row1[col4] = "";
row1[col5] = "";
row1[col6] = "";
row1[col7] = "";
row1[col8] = "";
row1[col9] = "";
row1[col10] = "";
row1[col11] = "";
row1[col12] = "";
table.Rows.Add(row1);
string stock = StockData.stock;
int rowID = (table.Rows.Count - 1);
string exchange = StockData.exchange;
Thread bh = new Thread(delegate()
{
Tick7(stock, rowID, exchange);
});
bh.SetApartmentState(ApartmentState.STA);
bh.IsBackground = true;
bh.Start();
StockData.flag = -1;
}
}
The Tick7 method which is being called is declared like this
[STAThread]
public void Tick7(string stock, int rowID, string exchange)
{
int rowNum = rowID;
int counter = -1;
deletecounter = StockData.deletecounter;
Thread.CurrentThread.Name = StockData.stock;
.
.
.
}

Resources