I have a simple application that has a single page with a button that invokes the CameraCaptureTask in Windows Phone 7. I use the MVVM pattern for this. My code behind is clean and I have offloaded the button click response to the ViewModel using behaviors. My code looks like this:
public class MainViewModel : ViewModelBase
{
private readonly CameraCaptureTask cameraCaptureTask;
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
}
else
{
cameraCaptureTask = new CameraCaptureTask();
cameraCaptureTask.Completed += cameraCaptureTask_Completed;
CaptureCommand = new RelayCommand(() => CaptureImage());
}
}
public RelayCommand CaptureCommand { get; set; }
private void cameraCaptureTask_Completed(object sender, PhotoResult e)
{
if (e == null || e.TaskResult != TaskResult.OK)
{
return;
}
else
{
// TODO
}
}
private object CaptureImage()
{
cameraCaptureTask.Show();
return null;
}
}
Now when I run my application and hit the button that binds to the 'CaptureCommand' RelayCommand, I hit my breakpoint in the 'CaptureCommand()' method and it fires the 'Show()' method of the CameraCaptureTask resulting in my camera coming up on the device successfully. However once I complete taking a picture and hit 'Accept', the 'cameraCaptureTask_Completed(object sender, Photoresult e)' event handler never gets invoked. Am I missing something here?
Thanks in advance!
Why are you relaying this when you have just a show?
Try moving the cameraCaptureTask out of the Constructor and into an invoked method
Are you using the WPConnect tools when you do this with the debugger AND a device connected?
http://msdn.microsoft.com/en-us/library/gg180729(v=VS.92).aspx
The Windows Phone Connect Tool allows you to establish serial or USB connectivity to the device without running the Zune software. When you test an application on a physical Windows Phone device, usually the Zune software is running. However, you are not able to test applications that interact with the photo chooser and camera launcher tasks while the Zune software is running, because it locks the local media database.
Related
I am using the Microsoft.Practices.Prism.PubSubEvents.IEventAggregator default implementation to wire up view models to respond to the application closing event as follows:
public class MainViewModel
{
public MainViewModel( IEventAggregator eventAggregator, Shell shell)
{
_shell.Closing += OnApplicationClosing;
_eventAggregator = eventAggregator;
}
private void OnApplicationClosing(object sender, CancelEventArgs e)
{
//TODO: investigate why this is locking application.
_eventAggregator.GetEvent<ApplicationClosingEvent>().Publish(new CancelEventArgs());
}
}
When I wire this event, application hangs upon closing. WPF Application is using .NET 4.5 and Prism v5.0.0.0 with Prism.PubSubEvents 1.0.0.0
Anyone seen this before?
I have a Web browser control in my project. Works great! However if I loose connection to the internet then open the project, IE opens and shows the standard cannot display webpage.
I'd prefer the Web browser control in my project show this message and not pop up a IE browser window when the internet connection is lost.
Thanks!
You can do this by importing System.Net.NetworkInformation namespace. NetworkChange Class exposes a event called NetworkAvailabilityChanged which is responsible to notify the application on connection status change. Please find the below snippet. Please mark the answer if useful.
public partial class MainWindow : Window
{
public bool IsAvailable { get; set; }
public MainWindow()
{
InitializeComponent();
NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
}
void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
IsAvailable = e.IsAvailable;
}
private void BrowseButton_Click(object sender, RoutedEventArgs e)
{
if (IsAvailable)
{
WebBrowser1.Navigate(TextBox1.Text);
}
else
{
MessageBox.Show("Your Popup Message");
}
}
}
I don't see IE open on my machine, but I do see the normal IE error messages displaying within the WebBrowser control.
I believe you could detect that the webpage wasn't loaded properly by handling the WebBrowser's Navigated event, and looking at the document's url property. Here is some XAML:
<WebBrowser Source="http://www.google.com" Navigated="WebBrowser_Navigated" />
And a bit of code (I don't do VB, sorry):
private void WebBrowser_Navigated(object sender, NavigationEventArgs e) {
var browser = sender as WebBrowser;
if (browser != null) {
var doc = browser.Document as HTMLDocument;
if (doc != null)
MessageBox.Show(doc.url);
}
}
On my machine, when the navigation failed, I got this URL:
res:ieframe.dll/navcancl.html#http://www.google.com
While I don't think we could count on the URL being exactly this all the time, I bet you could inspect it and determine that it's NOT the URL you were looking for. In fact, the "http:" is now "res:". When you see this happen (and don't expect it) you could make the browser point to a local source to display a message.
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 am having some issues with WPF not fully repainting a button control when the button is changed from another thread, and I am not sure how to force it to do a full repaint.
The situation is that on receipt of a message (via WCF - but the source isn't important, except that it is an external thread) I update the foreground color and visibility of a button. WPF immediately repaints the text on the button face, but the surface of the button is not repainted until I click anywhere on the application.
I have tried calling InvalidateVisual() on the button, but that did not help. I think that I am not understanding how a background thread can force a repaint. But the frustrating thing is that something is getting repainted and every other control I am using (text and image controls) are also getting properly repainted when I update them from my same message receipt.
I have now tried sending an empty message to the Dispatcher of the application via Invoke(), but no luck there either.
So I am looking for tips on how to tell WPF that it needs to update the rest of the button and not just the text.
Edit
This is a rough skeleton of my program. Note that I have wrapped the button in a class as there is other related state information I am keeping with it.
class myButton
{
Button theButton
void SetButton()
{
theButton.Forground = a new color
}
}
main
{
myButton.theButton = (Button on WPF canvass)
RegisterCallback( mycallbackFunction) with WCF client endpoint
}
void myCallbackFunction(message)
{
if message has button related stuff, call myButton.SetButton
}
Edit 2
Solved my problem .. it was actually a conflict between a "CanExecute" method and setting the buttons attributes in the callback. Once I removed the "CanExecute" function it all worked.
Setting properties on the button itself from code, especially another thread/callback, is an entrance to a painful world of inconsistent states.
What you should do is bind your button's properties to properties in your code, and then have your callback change those external properties.
I know the code you posted was kind of a mock up for what you actually want to do in your program, and I couldn't really follow your logic, but here's a complete program that operates similarly to your example and shows what I'm talking about. Let me know if I've missed the mark.
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///
public class MyButton : INotifyPropertyChanged
{
private Button _theButton;
public Button TheButton
{
get { return _theButton; }
set
{
_theButton = value;
//set text binding
Binding textBind = new Binding("Text");
textBind.Source = this;
textBind.Mode = BindingMode.OneWay;
_theButton.SetBinding(Button.ContentProperty, textBind);
//set color binding
Binding colorBind = new Binding("Brush");
colorBind.Source = this;
colorBind.Mode = BindingMode.OneWay;
_theButton.SetBinding(Button.ForegroundProperty, colorBind);
NotifyPropertyChanged("TheButton");
}
}
public void Set(string text, Brush brush)
{
this.Text = text;
this.Brush = brush;
}
private string _text;
public string Text
{
get { return _text; }
set { _text = value; NotifyPropertyChanged("Text"); }
}
private Brush _brush;
public Brush Brush
{
get { return _brush; }
set { _brush = value; NotifyPropertyChanged("Brush"); }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public partial class MainWindow : Window
{
MyButton _myButton = new MyButton();
public MainWindow()
{
InitializeComponent();
//button1 is defined in XAML markup
_myButton.TheButton = this.button1;
//or else this could be your callback, same thing really
Thread t = new Thread(SetButton);
t.Start();
}
void SetButton()
{
_myButton.Text = "wo0t!";
_myButton.Brush = Brushes.Red;
//or
_myButton.Set("giggidy!", Brushes.Yellow);
}
}
}
Note that binding your Button properties in XAML is much less ugly, but then we're getting into UserControls and DataContexts which is another topic. I would look at inheriting the Button class to implement the features you want.
I recommend reading the article (Build More Responsive Apps With The Dispatcher) from MSDN magazine that describes how WPF works with the Dispatcher when using BackgroundWorker.
As per my edit, I had conflict between the buttons CanExecute binding in the XAML and me setting the background color in the callback. I didn't really need the CanExecute, so getting rid of that solved my problem.
Greetings! Am enjoying using MVVM light -great framework - has made my life much easier, and has removed a number of barriers that were proving difficult to overcome....
Question:
I am attempting to setup a custom dialog box for editing messages users send to each other. I am attempting to construct a silverlight custom dialog box using the ChildWindow object using the MVVM framework.
Was wondering if there were any suggestions as to how this might be accomplished
Following the dialog MVVM sample code I found here: http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338 I got stuck because the ChildWindow dialog object in Silverlight is async, and has a different Result class.
So - the Basic idea I have now is using the view model of the class (in this case the Matrix.MessageViewModel) to create an instance of the custom dialog box, send it through the Messenger.Send<>, process the registered message in the view to display the dialog, then have the ChildWindow dialog box's Save button handler fire a Messenger.Send with the modified contents that is then stored using the Save method on the viewmodel...
Seems a bit round-about - so wanted to make sure there wasn't a cleaner way....
Relevant code bits:
view model:
messageDialogBox = new MessageEditorDialog(
selectedMessage, this.SelectedSiteId, this.LoggedOnEmployee.Id, this.Projects);
DialogMessage editMessage = new DialogMessage(
this, messageDialogBox,"Edit Message", DialogMessageCallback);
Messenger.Default.Send(editMessage);
View:
public ViewHost()
{
InitializeComponent();
Loaded += new RoutedEventHandler(ViewHost_Loaded);
if (!ViewModelBase.IsInDesignModeStatic)
{
// Use MEF To load the View Model
CompositionInitializer.SatisfyImports(this);
}
ApplicationMessages.IsBusyMessage.Register(this, OnIsBusyChange);
Messenger.Default.Register<DialogMessage>(this, msg => ShowDialog(msg));
}
private void ShowDialog(DialogMessage msg)
{
MessageEditorDialog myDialog = (MessageEditorDialog) msg.Target;
myDialog.Show();
}
Dialog Save:
private void ButtonSave_Click(object sender, RoutedEventArgs e)
{
Messenger.Default.Send<Message>(
this.MessageItem, CommandMessages.MessageTypes.MessageSave);
}
This ties back into the ViewModel, that has a Messenger.Default.Register<> watching for the CommandTypes.MessageSave which routes the resulting MessageItem to the model for storage.....
That's pretty darn close to what I'd do, except there are a couple of things I do differently.
I'd have a view model for my dialog view, and move the messaging logic to it rather than the view's code behind.
I'd use a Save command in my view model, and bind the ButtonSave to that command. That moves the save logic to the view model instead of the code behind of your view.
You're using a different message when the save button is clicked. Also, you're not using the DialogMessage's callback. Assuming you change to using a Save command, you could save the message in a private member in the view model, then use message's callback when the user saves.
You may want to think about re-using the dialog view, or ensuring that the view is being cleaned up correctly so you don't end up with a memory leak.
Here's the changes I'd make to your view model following suggestions 2 & 3.
public class MessageEditorDialogViewModel : ViewModelBase
{
private DialogMessage _dialogMessage;
public RelayCommand SaveCommand { get; private set; }
public DialogMessage Message { get; set; }
public MessageEditorDialogViewModel()
{
SaveCommand = new RelayCommand(SaveCommandExecute);
}
private SaveCommandExecute()
{
Message.Execute();
}
}