I'm making a Silverlight application and I'm using a MediaElement to play a video from the user's disk that I know the path of (say, "C:/foo.MOV"). I'd like a Javascript trigger from the browser to change the source of the MediaElement to another known file (eg "C:/bar.MOV"). I can make a button to do this in Silverlight, and I can have a Javascript trigger execute code inside the Silverlight app, but when I do, the MediaElement appears empty.
I've even tried having the Javascript call the btnLoadNewMediaTest_Click event, and while that event works fine called from user clicks on the button, it doesn't affect the media at all when called from outside the app.
Looking at the MediaElement in the debug, it seems that when it's called from the Javascript the MediaElement's Source appears as null and it seems to have made an empty copy.
I can confirm the Javascript is triggering the events in Silverlight, as it trips breakpoints in the Silverlight code.
I have managed to solve this: I created an EntryPoint class that is scriptable from JavaScript. When the JavaScript sendCommand is triggered, it puts a command and args into a queue held by the entry point. Every tick of a timer in the Silverlight app, the app checks the Count() of the queue and gets any commands and processes them.
From the Javascript, I call silverlightControl.Context.EntryPoint.setCommand("commandname", "args").
In the EntryPoint I have
[ScriptableMember()]
public string setCommand(string commandValue, string argsValue)
{
commands.Enqueue(commandValue);
args.Enqueue(argsValue);
commandWaitingFlag = true;
return Application.Current.HasElevatedPermissions.ToString();
}
In the Silverlight itself, I have a DispatcherTimer with an interval of 100ms. This has a tick event:
public void Each_Tick(object o, EventArgs e)
{
//Other code
if (entryPoint.commandWaitingFlag)
{
handleEntryPointCommands();
}
}
From inside handleEntryPointCommands I call a method of the entryPoint, getCommand():
public string[] getCommand() {
string commandOut = string.Empty;
string argsOut = string.Empty;
if (commands.Count > 0)
{
commandOut = commands.Dequeue();
argsOut = args.Dequeue();
if (commands.Count == 0)
{
commandWaitingFlag = false;
}
return new string[2] { commandOut, argsOut };
}
else
{
return new string[2];
}
}
and then can use the command I've gotten however I like. Hopefully that's more helpful with some code.
Related
I have to work on a C# GUI application that displays information regarding an alternator that sends back messages every 20ms. So whenever I receive a certain message for ex regarding the temperature, I have to update a Textbox's text property on the UI with the correct information. The problem is I have tried using multithreading with delegates, did not work, and using invoke's did not work. Using a thread pool didn't work. I just want a chunk of code that forces the UI to update a textbox's text property and never thought this could be that hard.
At the moment I have this:
delegate void UpdateTextBoxTextDelegate(double newValue);
private void UpdateTextBoxText(double newValue)
{
if(tbxTemp.InvokeRequired)
{
UpdateTextBoxTextDelegate del = new UpdateTextBoxTextDelegate(UpdateTextBoxText);
tbxTemp.Invoke(del, new object[] {newValue});
}
else
{
tbxTemp.Text = Convert.ToString(newValue + "°C");
}
}
private double alternatorTemperature;
public double _alternatorTemperature
{
get { return alternatorTemperature; }
set
{
alternatorTemperature = value;
UpdateTextBoxText(alternatorTemperature);
}
}
And then I have this function which is basically executing every time a CAN message is received with a certain PGN. This function call hierarchy basically leads back up to the main thread of the application, not the UI thread if that helps.
//Alternator Temperature
case 0xFEA7:
temp = (UInt16)(canMessage.Data4);
alternatorTemperature = Convert.ToDouble(temp);
_alternatorTemperature = alternatorTemperature;
break;
The following code changes the textbox text almost 20 seconds later after there have been thousands of messages sent which is not acceptable. I have also tried the invoke way but only has the same delayed result
tbxRPM.Invoke(new Action(() => tbxRPM.Text = alternatorRPM.ToString() + "/rpm"));
If anyone has an easy way of changing a textbox.text property and then have it immediately show up on the UI, it would be great.
I would recomend you to create a class representing your "Alternator" with a property for "Temperature" and one method for "GetTemperature" that would be the same has your "function which is basically executing every time a CAN message is received with a certain PGN". Once it is done, bind your textbox to the instatiated class and property using:
tbxTemp.DataBindings.Add("Text",
objAlternator,
"Temperature",
false,
DataSourceUpdateMode.OnPropertyChanged);
In the end, the method I used to force UI controls to change even though the main thread was busy all the time was the following. I used this method on text boxes and it turned out to be very responsive
private string _someVariable = "Initial text";
public string SomeVariable
{
get { return _someVariable; }
set { _someVariable= value;
if(PropertyChanged != null)
{
PropertyChanged(this,new System.ComponentModel.PropertyChangedEventArgs(SomeVariable));
}
}
}
Within the main form's constructor I added the following:
lblTextBox.DataBindings.Add("Text", this, "SomeVariable");
And then to trigger changes on the UI controls when I needed I just changed the text property of the textbox you bound the data to and it just worked without the need for multi-threading, delegates, or any complex stuff.
eg.
Where you need a control to change:
lblTextBox.Text = "Some text";
I created a custom Toast Notification for my Winforms application, whenever a custom result is returned, I use a ShowMessage extension to display the notification.
On my login screen, I have a unlock application button that will show the toaster message whenever the password is incorrect.
When I put a breakpoint right before the result.ShowMessage(); the notification appears. But when I remove the breakpoint, it does not appear anymore. I do not change any debug values.
How can I resolve this? I did try implementing a thread.sleep but it did not change anything, the notification only appears when I insert a breakpoint and continue over it.
It is worth noting that the notification works perfectly everywhere else in the application.
Code for the Unlock Application:
private void UnlockApplication()
{
var result = new Business.Server.User().Get(_loginModel.UserName, _loginModel.Password.Encrypt());
if (result.IsSuccessful)
{
// Perform log in process
}
else
{
result.ShowMessage(); //Only works when I put breakpoint here
}
}
Code for ShowMessage extension
public static void ShowMessage<T>(this Models.Result<T> result) where T : class
{
Helpers.ToasterNotificationHelper.ShowNotification(result.ResultTypeKey.ToDescription(), result.Message, result.ResultImage(), result.ResultColor(), result.ResultTypeKey == Enums.ResultTypeEnum.Warning ? 5000 : 2500);
}
public static void ShowNotification(string header, string message, Image icon, Color backgroundColor, int durationInMilliseconds = 1000)
{
if (Application.OpenForms[0] is MyApplication)
{
var toasterNotification = new ToasterNotificationControl(header, message, icon, backgroundColor);
(Application.OpenForms[0] as MyApplication).toastNotificationCollectionControl1.AddNotification(toasterNotification, durationInMilliseconds);
}
}
My Toast Notification control is a DevExpress flyout panel control that gets added to the Main application form
Your "Get(_loginModel.UserName ..." seems to be asynchronous. Try to do the next to make the call awaitable:
private async System.Threading.Tasks.Task UnlockApplicationAsync()
{
var result = await new Business.Server.User().Get(_loginModel.UserName, _loginModel.Password.Encrypt());
...
I have started working with WPF MVVM Light and now I'am trying to navigate between pages.
In the MainWindow I have added a "BackButton"
<Button Command='{Binding Main.GoBack, Mode=OneWay}' />
which is binding to MainViewModel method "RelayCommand GoBack".
private RelayCommand _goBack;
public RelayCommand GoBack
{
get
{
return _goBack
?? (_goBack = new RelayCommand(
() =>
_navigationService.GoBack();
}));
}
}
Why is this button changing view only once? If I want to click it secound time
it doesn't work (nothing happend). If I change page for another by another button its starting work again and againg only for once.
Part of implementation of FrameNavigationService:
public FrameNavigationService()
{
_pagesByKey = new Dictionary<string, Uri>();
_historic = new List<string>();
}
public void GoBack()
{
if (_historic.Count > 1)
{
_historic.RemoveAt(_historic.Count - 1);
NavigateTo(_historic.Last(), null);
}
}
public void NavigateTo(string pageKey)
{
NavigateTo(pageKey, null);
}
public virtual void NavigateTo(string pageKey, object parameter)
{
lock (_pagesByKey)
{
if (!_pagesByKey.ContainsKey(pageKey))
{
throw new ArgumentException(string.Format("No such page: {0} ", pageKey), "pageKey");
}
var frame = GetDescendantFromName(Application.Current.MainWindow, "MainFrame") as Frame;
if (frame != null)
{
frame.Source = _pagesByKey[pageKey];
}
Parameter = parameter;
_historic.Add(pageKey);
CurrentPageKey = pageKey;
}
}
What can I do to handle this? May be I should do it tottaly differently?
You should possibly not be doing goback at all.
Unless you really want to use the journal, using a frame and pages is a bad idea. It's a rare requirement to go back to the last view in desktop apps. What with them not being a web browser.
Maybe you have that requirement though.
If you have a frame then you have it's journal and you can just call goback on the frame's navigationservice.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.navigation.navigationservice.goback?view=netframework-4.8
You set keepalive on pages.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.page.keepalive?view=netframework-4.8
You wrote that code and it seems to be largely reproducing navigationservice functionality. From what you've shown us.
As it is.
Use type rather than a magic string as the key. A type is checked at compile time, a magic string is not and you can make mistakes.
Have you explored this issue at all? I think maybe this is one of those times that telling someone what they did wrong isn't really helping as much as telling them how they ought to diagnose.
Debugging is a key skill for any developer.
You have the code running in front of you.
Put break points in, step through and examine what is happening.
When you navigate, what ends up in _historic?
When you goback, what happens exactly?
When you click the goback that second time what path does it go down and what state is causing that.
Make sure you are using RelayCommand in GalaSoft.MvvmLight.CommandWpf,not at GalaSoft.MvvmLight.Command.RelayCommand
Although this has been posted before on StackOverflow but i think none of those reflect my issue and none of those solutions work for me either. So i'm developing a Windows Phone app and my workflow is a bit like this:
App starts
ContactPicker opens up
User selects one or multiple contacts
Based on how many contacts he selected, that many PivotItems are added into the Pivot.
My code is as follows:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// TODO: Prepare page for display here.
// TODO: If your application contains multiple pages, ensure that you are
// handling the hardware Back button by registering for the
// Windows.Phone.UI.Input.HardwareButtons.BackPressed event.
// If you are using the NavigationHelper provided by some templates,
// this event is handled for you.
SelectContacts();
}
private async Task SelectContacts()
{
var picker = new ContactPicker();
picker.DesiredFieldsWithContactFieldType.Add(ContactFieldType.PhoneNumber);
ContactsList = (List<Contact>)await picker.PickContactsAsync();
DisplayContacts();
}
private void DisplayContacts()
{
if (ContactsList != null)
{
foreach (var item in ContactsList)
{
PivotItem pivotItem = new PivotItem();
pivotItem.Header = item.FirstName.ToString();
ContentRoot.Items.Add(pivotItem);
}
}
}
According to me, in SelectContacts() method, the app should wait at the await call and once it gets back the list of contacts, than it should execute the DisplayContacts() method but its not working. I've tried multiple other variations of this code and they aren't working either.
await the SelectContacts() method and add the DisplayContacts() method beneath it. Remove the DisplayContacts() method from SelectContacts()
await SelectContacts();
DisplayContacts();
I don't know the complete reason why but i figured it out that since i was making the PickContactsAsync() call in the OnNavigatedTo() event, that is why it wasn't working as expected. Once i moved the PickContactsAsync() call into the PageLoaded() event handler, it started working as usual.
When the user touch the app icon,
I want do these steps before user go to the main view
Fetch json string from URI
Use JArray.Parse to get the value
After all finish, go to the main view.
The problem is how can I prevent user to go to the main view
and put all the code
I tried to put it in Application_Launching method in the App.xaml.cs file
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
// code here
}
But it doesn't prevent the program to go to the main view before the fetching finished.
And I found that actually in the MainPage.xaml, if I put this code like this
protected override void OnNavigatedTo(NavigationEventArgs e)
{
while(true) {}
// it will prevent the program to go to the main view,
// and will stick with the loading screen until this function reach its end
}
So I think, I can put the all the code here, when I finish the fetch, I will just break the while and it will go to the main view automatically.
And I try, this is the code
protected override void OnNavigatedTo(NavigationEventArgs e)
{
bool isFetchFinished = false;
ObservableCollection<PromoViewModel> Promos = new ObservableCollection<PromoViewModel>();
WebClient client = new WebClient();
client.DownloadStringCompleted += (s, evt) =>
{
if (evt.Error == null)
{
// Retrieve the JSON
string jsonString = evt.Result;
JArray promos = JArray.Parse(jsonString);
foreach (JObject promo in promos)
{
string name = promo["name"].Value<string>();
string description = promo["description"].Value<string>();
string img = promo["image"].Value<string>();
Promos.Add(new PromoViewModel() { Name = name, Description = description, Img = img });
}
isFetchFinished = true;
System.Diagnostics.Debug.WriteLine("finish fetch");
}
};
// run
client.DownloadStringAsync(new Uri("the json url"));
while(true) {
if(isFetchFinished) {
App.ViewModel.LoadData(Promos); // pass value to main view model
break; // after complete, break
}
}
}
I thought it would work, but it was not.
This is what I found,
The WebClient DownloadStringAsync won't run until the OnNavigatedTo function finished.
Because it's still waiting for the while loop to break and reach the end function.
And this
isFetchFinished = true; // will never executed
Resulting infinite loop.
I think I put the fetch code in the wrong method. Where is the right place to put all of this?
Ouch, you are doing it all wrong. First of all, you have to specify the starting page. If you want to download some data before navigating to it, you can create a special "download" page that is actually the first page navigated to when starting the application. And then, once the download is completed, you navigate to your main page. This is actually a replacement for the extended splash screen.
Also, never put while (true) in any UI code, that will simply freeze the application. Besides, if the application is frozen, you never get the chance to "unfreeze" it.