new to WPF and c# hobbyist...
For some reason, I can't get my loadingAnimation (or any other) function to run immediately after a button press and before a SOAP call.
My xaml:
<Grid>
<TextBox Height="220" HorizontalAlignment="Left" Margin="12,79,0,0" Name="txtResults" VerticalAlignment="Top" Width="337" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,29,0,0" Name="txtServiceTag" VerticalAlignment="Top" Width="120" />
<CheckBox Content="This computer's service tag" Height="16" HorizontalAlignment="Left" Margin="151,32,0,0" Name="chkThisST" VerticalAlignment="Top" Checked="chkThisST_Checked" Unchecked="chkThisST_Unchecked"/>
<Button Content="Get Info" Height="23" HorizontalAlignment="Left" Margin="12,324,0,0" Name="btnGetInfo" VerticalAlignment="Top" Width="75" Click="btnGetInfo_Click" />
<my:LoadingAnimation HorizontalAlignment="Center" Margin="128,154,419,127" VerticalAlignment="Center" Name="loadingAnimation" Visibility="Hidden" />
</Grid>
My .cs:
private void btnGetInfo_Click(object sender, RoutedEventArgs e)
{
txtResults.Text = "Retrieving information...";
ShowHideLoading();
SoapCall();
ShowHideLoading();
}
My SoapCall() seems to be running before txtResults.Text has time to populate. SoapCall() takes about 5 seconds to return a message. I've messed around with the order of objects in , but to no avail.
Any help is appreciated!
The reason is that the SoapCall() is blocking the UI thread. In other words, until it is finished - no UI operations will be called.
You can solve this by putting the SoapCall() inside a BackgroundWorker.
Then, the ShowHideLoading can be put inside the RunWorkerCompleted event.
Here is an example on how to use the BackgroundWorker
When you perform actions on the main thread (in this case, the thread that calls the _Click method.), the UI will not actually update until the call finishes, and the framework alerts the UI to redraw itself. The reason you're never seeing the update is because the SoapCall is blocking the main thread from performing any updates.
For this to work properly, I would recommend updating the UI, forking a new thread to perform the Soap call, then when the operation completes, reset the UI back to its original state.
You may also want to look at how to use the Dispatcher object correctly, because WPF requires that all UI updates be made on the main thread. The Dispatcher allows you to force a snippet of code to run on a particular thread.
Related
I'm using WPF alongside Caliburn.Micro. I want any code to be run when a label is clicked. I tried some googling and found out about cal:Message.Attach.
XAML:
<Label x:Name="Info" Content="Info" HorizontalAlignment="Left" Margin="305,440,0,0"
VerticalAlignment="Top" FontFamily="Tahoma" FontSize="10" FontWeight="Bold" cal:Message.Attach="[Action ShowAboutWindow()]"/>
C#:
public void ShowAboutWindow()
{
MessageBox.Show("xyz"); // just to test whether ShowAboutWindow is executed whatsoever (see explanation below)
WindowManager.ShowWindow(new AboutViewModel(EventAggregator, WindowManager, SettingsManager));
}
However, ShowAboutWindow isn't run whatsoever. I added a MessageBox to make sure that it isn't the WindowManager screwing it up.
How can I achieve what I desire?
EDIT 1: What I had tried even before was adding a public void Info() method to the ViewModel, as this works for buttons. But it didn't in this case.
Try attaching to the MouseLeftButtonUp event to simulate a click event after the mouse left button is released.
cal:Message.Attach="[Event MouseLeftButtonUp] = [Action ShowAboutWindow()]"
I am writing a program that corrects values in text files. In my xaml I have a textbox that is supposed to report the status/progress. "waiting"=before program starts, "processing"= program is processing files, "done"= files have been processed.
Here is my xaml code for the text field and the RUN button:
<TextBox x:Name="statusBox" HorizontalAlignment="Left" Height="23" Margin="130,332,0,0" TextWrapping="Wrap" Text="waiting" VerticalAlignment="Top" Width="120" FontSize="14" TextAlignment="Center" IsReadOnlyCaretVisible="True" >
<Button Content="Run"
Name="Run"
HorizontalAlignment="Left"
Margin="562,27,0,0"
VerticalAlignment="Top"
Width="53"
Click="Run_Click"
RenderTransformOrigin="-0.305,0.487" Height="19">
<Button.BindingGroup>
<BindingGroup Name="btnOpen"/>
</Button.BindingGroup>
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-0.848"/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
</Button>
Here is the problem I am having:
I have tried this many different ways. What I have below is the most recent attempt.
//In XAML, the status defaults to "waiting".
public class Operate
{
public void operations()
{
statusBox.Text = "processing";
... Perform Text Operations
status.Text = "finished";
}
}
When I run the program, I get see "waiting" in the status text box. However, when I click Run, the program begins but the text still says "waiting". Then, when the program has finished running, the text changes to "finished" without changing to "processing" in between.
Any help is greatly appreciated. I have programmed many console applications, but am relatively new to wpf.
Thanks.
When you start running, your UI won't be updated until it ends running the current thread.
After completing the task, your status will be updated very fast from processing to finished, so you won't see the change.
I recommend using a background worker to perform longer operations and still have the UI operational.
See this howto: Use a background worker
You may want to take a look into using a BackgroundWorker. It appears the UI is being blocked while processing your requests.
MSDN BackgroundWorker Class
As others have said the problem is that all your code is executing on the same thread. Your operations are running on the UI thread, which means it cannot respond to things like changes as you are trying to do.
The way around this is move your work onto a new thread.
(This assumes a newer version of the .NET framework that supports async/await syntax)
public class Operate
{
public async void operations()
{
statusBox.Text = "processing";
await Task.Run(()=>{
//do work here
});
status.Text = "finished";
}
}
Keep in mind that since that Task.Run command executes on a different thread it cannot access the UI. If you need it to have some information from the UI, you can pass it in the Task.Run call like so:
await Task.Run(mydata=>{
//do stuff, with mydata variable
});
If you need to return something to display on the UI then you can do this:
var myReturnValue = await Task.Run(mydata=>{
//do work
return myNewReturnData;
});
//can do something with your return data here.
If you need the ability for the user to click a different button to cancel the transaction look up the CancellationToken class.
My program should play a video when the user presses the 'Play' button. While it normally does this, the very first time they press the 'Play' button nothing will happen.
I have traced this bug back to the following code, which sets my MediaElement 'VideoPlayer':
public void playVideo_Tapped(object sender, TappedRoutedEventArgs e)
{
setUpVideo();
VideoPlayer.Play();
}
public async void setUpVideo()
{
if(vm == null) return;
StorageFile videoFile = vm.videoFile;
if (videoFile == null || !videoFile.ContentType.Equals("video/mp4")) return;
using (IRandomAccessStream fileStream = await videoFile.OpenAsync(FileAccessMode.Read))
{
VideoPlayer.SetSource(fileStream, videoFile.ContentType);
}
}
The culprit seems to be the 'SetSource()' method at the end. The only variable that changes from the first click of 'Play' to the next is the variable 'VideoPlayer.PlayToSource', which is changed from null to a real value.
(As a side note, the variable 'VideoPlayer.CurrentState' also changes from 'Closed' to 'Opening' but resets itself to 'Closed' before the second click. Only 'PlayToSource' changes the functionality.)
I figured that I could do a quick-fix by doing this in my first method:
setUpVideo();
setUpVideo();
VideoPlayer.Play();
Not great code but it ought to set things straight, right? Nope! This causes a NullReferenceException. On the second call to 'setUpVideo()' I find that 'PlayToSource' still has a value and 'VideoPlayer.CurrentState' is still set to 'Opening'... which somehow triggers the NullReferenceException.
I'm expecting the solution to be one of the following things:
1.) Set 'VideoPlayer.PlayToSource' on the first click before calling 'SetSource'.
2.) In the quick-fix, set 'VideoPlayer.CurrentState' back to 'Closed' in between calls.
3.) Some other thing that mimics what the first click is doing.
Of course, both of my ideas involve changing a read-only variable. Which is where I'm getting stuck. I'll include the .xaml code for good measure, but I'm confident that it's the method 'SetSource' that's the root of my troubles:
<Grid x:Name="VideoViewerParentGrid" Background="DarkGreen" Height="{Binding VideoViewerParentGridHeight }" Width="{Binding VideoViewerParentGridWidth}">
<MediaElement x:Name="VideoPlayer" HorizontalAlignment="Center" VerticalAlignment="Bottom" Stretch="Uniform"
Visibility="{Binding VideoVisibility, Converter={StaticResource visibilityConverter}}"/>
<Button Style="{StaticResource BackButtonStyle}" Tapped="VideoViewerClose_Tapped" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Button Name="Play_Button" Content="Play Video" FontSize="26" Tapped="playVideo_Tapped"
VerticalAlignment="Top" HorizontalAlignment="Left" Height="60" Width="180" Margin="0,80,0,0"/>
</Grid>
---- UPDATE ----
Some more poking has revealed that on the first click 'VideoPlayer.CurrentState' never reaches the 'Playing' state, instead going from 'Opening' right back to 'Closed'. It does not do this on any subsequent clicks for as long as the program is running. Still investigating the cause of this.
You are missing the "await" keyword. Do this:-
await setUpVideo();
Short version, this issue is fixed by changing this:
using (IRandomAccessStream fileStream = await videoFile.OpenAsync(FileAccessMode.Read))
{
VideoPlayer.SetSource(fileStream, videoFile.ContentType);
}
...to be this:
IRandomAccessStream fileStream = await videoFile.OpenAsync(FileAccessMode.Read);
VideoPlayer.SetSource(fileStream, videoFile.ContentType);
Longer version, my code was failing because of the error "mf_media_engine_err_src_not_supported hresult - 0xc00d36c4", which was closing my MediaElement instead of playing it. This was happening because when I left the 'using' block of code the 'IRandomAccessStream' would close in the middle of my reading of the file. I'm not 100% clear why it gets through the whole thing after the first run of the code, but at least it now works reliably.
I've also got to give credit where credit is due, and I found this answer here: Windows 8 app - MediaElement not playing ".wmv" files
I'm using the wpf toolkit busy indicator which provides an overlay on top of my UI while a background operation is taking place. There's a progress bar in the control which is set to indeterminate which is fine while my background task is going on. Once the background task is complete, the UI needs to update which can take 1-2 seconds. This of course causes the progress bar to freeze which looks ugly.
My question is, how can I spin up the busy indicator on a background thread so that the progress bar carries on moving all the way up until the UI becomes responsive? I'm open to other solutions as well as long as the progress bar doesn't freeze.
Here's some sample code:
<xctk:BusyIndicator IsBusy="{Binding IsBusy}" Style="{StaticResource BusyIndicatorStyle}">
<DockPanel Margin="3">
<TextBlock DockPanel.Dock="Top" Style="{StaticResource WorkspaceHeaderStyle}" Text="User Management"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ToolBar Grid.Row="0">
<Button Content="Save changes" Command="{Binding SaveChangesCommand}"/>
</ToolBar>
<TabControl Grid.Row="1">
<TabItem Header="Users" DataContext="{Binding UsersViewModel}">
<users:UsersView />
</TabItem>
<TabItem Header="Roles" DataContext="{Binding RolesViewModel}">
<roles:RolesView />
</TabItem>
</TabControl>
</Grid>
</DockPanel>
</xctk:BusyIndicator>
private void LoadDays()
{
ProgressIsBusy = true;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var loadDaysTask = GetLoadDaysTask(uiScheduler);
loadDaysTask.ContinueWith(antecedent =>
{
RaisePropertyChanged(() => ForecastViewModel);
RaisePropertyChanged(() => AverageHandleTimeViewModel);
RaisePropertyChanged(() => GeneralOptionsViewModel);
RaisePropertyChanged(() => ScheduledHoursViewModel);
IsUserEditing = true;
ProgressIsBusy = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
loadDaysTask.Start();
}
No, It is not possible to show the BusyIndicator animations when UI is updating / using Background thread or something.
Refer the related post here.
busy indicator during long wpf interface drawing operation
You can show some static image to indicate the busy sign until your UI updation complete.
I don't believe that you can do that. You can't access UI controls from a background thread. Also, you may find that it is the UI thread that is busy rendering or notifying and making the progress bar freeze.
I have a similar setup in a large scale WPF application and it suffers from the same problem. The busy indicator displays fine while data is being fetched from the database, but then when the application starts to render the data in the UI, the busy indicator freezes.
I even tried using an animated Gif to get around this issue, but of course they don't animate (by themselves) in WPF applications. Having written the code to animate the frames in the Gif, I was very disappointed to find that it also suffered from the same problem.
Good luck all the same.
<Button x:Name="btn_binding" Content="Binding" HorizontalAlignment="Right" Height="44" Margin="0,127,63,0" VerticalAlignment="Top" Width="67"/>
<TextBox x:Name="txt_binding" Text="{Binding Content,ElementName=btn_binding}" Height="48" Margin="0,48,31,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Right" Width="130"/>
it's result will be like this
Then i can get the same result by the below code
public partial class biding : Window
{
public biding()
{
this.InitializeComponent();
txt_binding.Text=btn_binding.Content.ToString();
}
}
please tell me what are the differences of both and i want to which one is best to use...
In first case you bind your TextBox.Text property to Button.Content property and it will be changed everytime the Content is changed. In second you just set Text property once in constructor and changing the Button.Content won't affect into it.
Hope it's clear.
In my point of view... Xaml binding will be reflected in the designer at the time when you typed... No need to wait to run the solution to see the output..
In case of code behind .. you need to run the solution to see the results..