This is a problem that has been bugging me a lot. I still haven't got a solution for this.
I have this XAML code, the DrawingSurfaceBackgroundGrid is an Unity game, and the rest is collapsed Camera.
<DrawingSurfaceBackgroundGrid x:Name="DrawingSurfaceBackground" Loaded="DrawingSurfaceBackground_Loaded">
<!-- Augmented Reality -->
<Canvas VerticalAlignment="Center" x:Name="arCameraStack" Canvas.ZIndex="1" Width="732" Height="549" HorizontalAlignment="Left" Visibility="Collapsed" Tap="viewfinderCanvas_Tap">
<Canvas.Background>
<VideoBrush x:Name="viewfinderBrush" />
</Canvas.Background>
<Image x:Name="imgTarget" Source="/Assets/Icons/camera.target.png" VerticalAlignment="Center" HorizontalAlignment="Center" Canvas.Left="114" Canvas.Top="27"/>
</Canvas>
</DrawingSurfaceBackgroundGrid>
These are my Start/Stop camera functions:
public void StartCamera(bool hasTarget)
{
camera = new PhotoCamera(CameraType.Primary);
viewfinderBrush.SetSource(camera);
Dispatcher.BeginInvoke(() =>
{
if (hasTarget)
{
imgTarget.Visibility = Visibility.Visible;
}
else
{
imgTarget.Visibility = Visibility.Collapsed;
}
});
}
public void StopCamera()
{
if (camera != null)
{
camera.Dispose();
camera = null;
}
}
I have a screen system that changes the views according to what you want to see, sorta like changing pages.
This gets called in the page that shows the Camera part.
public void Show()
{
MainPage.Instance.Dispatcher.BeginInvoke(() =>
{
MainPage.Instance.arCameraStack.Visibility = Visibility.Visible;
});
MainPage.Instance.StartCamera(false);
}
And this gets called when I want to hide the Unity part.
public void Hide()
{
UnityApp.SetNativeResolution(0, 0);
UnityApp.Obscure(false);
UnityApp.Deactivate();
MainPage.Instance.Dispatcher.BeginInvoke(() =>
{
MainPage.Instance.ApplicationBar.IsVisible = true;
});
}
Everything works when I have the solution build and run through Visual Studio, but once I start the app from the phone, not in Master/Debug mode from VS, the camera just hangs. I found out that when I toggle the visibility of anything in the page, the camera will update for a second, and then hang once again.
Well, I found out what was the problem.
My Show/Hide functions look like these now:
public void Hide()
{
UnityApp.Obscure(false);
UnityApp.Deactivate();
MainPage.Instance.Dispatcher.BeginInvoke(() =>
{
MainPage.Instance.DrawingSurfaceBackground.SetBackgroundContentProvider(null);
MainPage.Instance.DrawingSurfaceBackground.SetBackgroundManipulationHandler(null);
MainPage.Instance.HideUnityBorder.Visibility = Visibility.Visible;
MainPage.Instance.ApplicationBar.IsVisible = true;
});
}
public void Show()
{
var content = Application.Current.Host.Content;
var width = (int)Math.Floor(content.ActualWidth * content.ScaleFactor / 100.0 + 0.5);
var height = (int)Math.Floor(content.ActualHeight * content.ScaleFactor / 100.0 + 0.5);
UnityApp.SetNativeResolution(width, height);
UnityApp.UnObscure();
MainPage.Instance.Dispatcher.BeginInvoke(() =>
{
MainPage.Instance.DrawingSurfaceBackground.SetBackgroundContentProvider(UnityApp.GetBackgroundContentProvider());
MainPage.Instance.DrawingSurfaceBackground.SetBackgroundManipulationHandler(UnityApp.GetManipulationHandler());
MainPage.Instance.HideUnityBorder.Visibility = Visibility.Collapsed;
MainPage.Instance.ApplicationBar.IsVisible = false;
});
}
The main problem was in the DrawingSurfaceBackground.SetBackgroundContentProvider and DrawingSurfaceBackground.SetBackgroundManiuplationHandler functions.
Now switching between the Unity part and WP part is easy.
Related
I'm trying to create a Custom Spalsh Screen in WPF - Net 4.8 - C# to show a startup image with some messages that change during Loading process.
The Spalsh screen is in a separete Library. Here my code:
SPLASH SCREEN LIBRARY:
Window (xaml):
<Window ...
Title="SplashScreenWindow" Height="400" Width="610" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Topmost="True" WindowStyle="None" >
<StackPanel>
<Image x:Name="imgSplash" Grid.Row="0" Source="{Binding ConfigModel.DisplayedImage}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</StackPanel>
</Window>
Window (cs):
public SplashScreenWindow(string SplashImagePath)
{
InitializeComponent();
this.DataContext = new MySplashScreenViewModel(SplashImagePath);
}
SplashImagePath is the path to the Image. It is passed to the ViewModel. If it is null, default image should be loaded.
ViewModel:
public class SplashScreenConfigModel : ConfigModelBase
{
public BitmapImage DisplayedImage
{
get { return GetValue(() => this.DisplayedImage); }
set { SetValue(() => this.DisplayedImage, value);}
}
}
public class MySplashScreenViewModel : PropertyChangedNotificationBase
{
public MySplashScreenConfigModel ConfigModel
{
get { return GetValue(() => this.ConfigModel); }
set { SetValue(() => this.ConfigModel, value); }
}
public MySplashScreenViewModel(string SplashImagePath)
{
ConfigModel = new SplashScreenConfigModel();
if (SplashImagePath.IsEmpty()) SplashImagePath = "Media/DefaultImage.png";
ConfigModel.DisplayedImage = ImagesUtilities.LoadBitmapImageFromPath(SplashImagePath);
}
}
where MyImagesUtilities.LoadBitmapImageFromPath(SplashImagePath) is coded like that:
public static BitmapImage LoadBitmapImageFromPath(string path)
{
if (path.IsEmpty()) return null;
BitmapImage img = new BitmapImage();
img.DecodePixelWidth = 610;
img.BeginInit();
img.UriSource = new Uri(path, UriKind.Relative);
img.EndInit();
return img;
}
MAIN APP
The OnStartup is coded Like that:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
SplashScreenWindow splash = new SplashScreenWindow("Media/AppImage.png");
this.MainWindow = splash;
Task.Factory.StartNew(() =>
{
splash.Dispatcher.Invoke(() => splash.Show());
//Various activities...
this.Dispatcher.Invoke(new Action(() =>
{
MainWindow main = new MainWindow();
this.MainWindow = main;
main.Show();
splash.Close();
}));
});
}
}
When I start the app, the splash screen appears, but no image is shown.
But if I:
Put a breakpoint at the end of the SetSplashScreen method
breakpoint at the end of the SetSplashScreen method
Insert img.Height in the watches:
img.Height in watches
And the I run the app, and, when breakpoint, is reached, click on "Continue", first one of this two errors occur:
Exception message
or
Errors
But after, by clicking again on Continue, the Splash Screen appears with the correct Image.
Correct Splash
Images (both deafult and app image) are a .png images with Compile Action = "Content" and Copy in output directory = "Copy if newer".
How can I correct that? Any idea?
We have a problem with the ZXing Barcodescanner for Xamarin.Forms.
The scanner works perfectly on Android, but on IOS I can't see the camera image (preview). The scanner does scan barcodes on IOS if I hold them in front of the camera but the camera preview is just a white background.
I tried playing around with the options but without luck.
We are using Prism.Forms for MVVM.
As I mentioned, my code works well on android.
Here are some details:
The permissions are properly set on both platforms.
The NuGets ZXing.Net.Mobile and ZXing.Net.Mobile.Forms are added too
all three projects (Android, IOS and portable)
We are using .NET Standard 2.0
Xamarin.Forms is version 3.4.0
ScannerView.xaml
<forms:ZXingScannerPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:forms="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
x:Class="App.Portable.View.ScannerView">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<forms:ZXingScannerView x:Name="scanner" Grid.Column="0" Grid.Row="0" HorizontalOptions="EndAndExpand" VerticalOptions="FillAndExpand"
IsScanning="{Binding IsScanning}"
IsAnalyzing="{Binding IsAnalyzing}"
Result="{Binding Result, Mode=TwoWay}"
ScanResultCommand="{Binding CmdScanResult}"
Options="{Binding ScannerOptions}"
/>
<forms:ZXingDefaultOverlay Grid.Column="0" Grid.Row="0"
TopText="Some title"
ShowFlashButton="False"
BottomText="Some bottom text"
Opacity="0.9"/>
</Grid>
</ContentPage.Content>
ScannerViewModel.cs
public class ScannerViewModel : ViewModelBase
{
//Initializing variables
public ScannerViewModel()
{
var options = new MobileBarcodeScanningOptions();
options.TryHarder = true;
options.InitialDelayBeforeAnalyzingFrames = 300;
options.DelayBetweenContinuousScans = 100;
options.DelayBetweenAnalyzingFrames = 200;
options.AutoRotate = false;
ScanningOptions = options;
Title = "Barcode-Scanner";
CmdScanResult = new DelegateCommand(OnCmdScanResult);
IsScanning = true;
IsAnalyzing = true;
}
public MobileBarcodeScanningOptions ScanningOptions
{
get => _scanningOptions;
set => SetProperty(ref _scanningOptions, value);
}
public bool IsScanning
{
get => _isScanning;
set => SetProperty(ref _isScanning, value);
}
public bool IsAnalyzing
{
get => _isAnalyzing;
set => SetProperty(ref _isAnalyzing, value);
}
public Result Result
{
get => _result;
set => SetProperty(ref _result, value);
}
public DelegateCommand CmdScanResult { get; }
private void OnCmdScanResult()
{
IsAnalyzing = false;
IsScanning = false;
Device.BeginInvokeOnMainThread(
async () =>
{
IsAnalyzing = false;
var parameters = new NavigationParameters();
parameters.Add(CodeConstants.BARCODE, Result);
await NavigationService.GoBackAsync(parameters);
});
}
}
Does anyone see an issue at my code or has some suggestions on how to do it better or at least get it to work?
EDIT:
I uploaded a Testproject to my repo to reproduce the error. The Scanner works on iPhone but doesn't show the camera preview:
https://gitlab.com/mitti2000/zxingtest
Cause: You put the ZXingScannerView and ZXingDefaultOverlay in the same cell of grid .Then you set the HorizontalOptions of ZXingScannerView as EndAndExpand .
Solution:
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
I'm building an app with optional audio support, and it will feature some kind of background sounds (like a looping soundtrack that keeps on playing in every page).
What I did is to create an audio manager that allows me to manage my sounds, and this should mute/unmute the audio based on user's settings.
Unfortunately this doesn't happen, and the audio keeps on playing even if the user disables it.
This is a sample of my code:
public static async Task StartSoundManager()
{
// Get audio stream from app folder
// ...
BackgroundSound.SetSource(currentStream, currentFile.ContentType);
BackgroundSound.IsLooping = true;
ToggleSounds();
}
public static void ToggleSounds()
{
BackgroundSound.IsMuted = !Settings.IsAudioOn;
}
public bool IsAudioOn
{
// standard getter
set
{
// save value
SoundManager.ToggleSounds();
}
}
After some tests, IsMuted is set correctly (I've also tried setting volume to 0) but nothing happens when changing settings.
Do you guys have any idea on why such a simple task is not working as expected? It seems to me that you can't change volume after setting the source, and this feels really wrong.
EDIT: more complete class
public static class AudioManager
{
public const string BACKGROUND = "BACKGROUND.mp3";
private static readonly MediaElement BackgroundSound = new MediaElement();
public static async Task StartSoundManager()
{
// Get folder
var folder =
await (await Package.Current.InstalledLocation.GetFolderAsync("Assets")).GetFolderAsync("Audio");
var currentFile = await folder.GetFileAsync(BACKGROUND);
var currentStream = await currentFile.OpenAsync(FileAccessMode.Read);
BackgroundSound.SetSource(currentStream, currentFile.ContentType);
// Set mode and volume
BackgroundSound.IsLooping = true;
ToggleSounds();
}
public static void ToggleSounds()
{
BackgroundSound.IsMuted = !Settings.IsAudioOn; // IsAudioOn is false, still the sound plays
}
}
MediaElement is a XAML control, to make MediaElement.IsMuted property work, we need to add MediaElement into Visual Tree. For example, in your code, we can change BackgroundSound to a public field like:
public static readonly MediaElement BackgroundSound = new MediaElement();
And then in a page (e.g. MainPage) add it to the page:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
await AudioManager.StartSoundManager();
rootGrid.Children.Add(AudioManager.BackgroundSound);
}
After this, your ToggleSounds method should be able to work.
But since you want to keep on playing in every page, add MediaElement into the page may be not a good practice, here I'd suggest you use MediaPlayer class instead of MediaElement like:
public static class AudioManager
{
public const string BACKGROUND = "BACKGROUND.mp3";
private static readonly MediaPlayer BackgroundSound = new MediaPlayer();
public static void StartSoundManager()
{
BackgroundSound.Source = MediaSource.CreateFromUri(new Uri($"ms-appx:///Assets/Audio/{BACKGROUND}"));
BackgroundSound.IsLoopingEnabled = true;
ToggleSounds();
BackgroundSound.Play();
}
public static void ToggleSounds()
{
BackgroundSound.IsMuted = !Settings.IsAudioOn; // IsAudioOn is false, still the sound plays
}
}
For more info, please see Play audio and video with MediaPlayer.
I have a similar situation. I have a ToggleMenuFlyoutItem for turning sounds on and off app-wide that writes to the local settings:
Here is the XAML:
<Button IsTabStop="False" Style="{StaticResource qButtonStyleFinal}" TabIndex="2" x:Name="btnMore" BorderBrush="{x:Null}" Foreground="{x:Null}"
Content="1" Grid.Column="37" Margin="5" HorizontalAlignment="Center" Grid.Row="1" Grid.RowSpan="4" Grid.ColumnSpan="2" MaxWidth="60" MaxHeight="60">
<Button.Background>
<ImageBrush ImageSource="ms-appx:///Assets/more_project.png" Stretch="Uniform" />
</Button.Background>
<Button.Flyout>
<MenuFlyout>
<ToggleMenuFlyoutItem x:Name="mfiToggleSounds" Text="sound effects" IsChecked="{x:Bind Mode=TwoWay, Path=Model.Sounds}"></ToggleMenuFlyoutItem>
<MenuFlyoutItem x:Name="mfiExportExcel" Text="export to Excel" Tapped="mfiExportExcel_Tapped" />
<MenuFlyoutItem x:Name="mfiExportCSV" Text="export to CSV" Tapped="mfiExportCSV_Tapped" />
<MenuFlyoutItem x:Name="mfiEmailDeveloper" Text="email developer" Tapped="mfiEmailDeveloper_Tapped" />
</MenuFlyout>
</Button.Flyout>
</Button>
Changes are handled in the property set event:
private bool _sounds = true;
public bool Sounds
{
get => _sounds;
set
{
_sounds = value;
NotifyPropertyChanged();
var localSettings = ApplicationData.Current.LocalSettings;
localSettings.Values["sounds"] = _sounds;
}
}
Setting is loaded on the main page Page_Loaded event as follows:
// retrieve settings
var localSettings = ApplicationData.Current.LocalSettings;
if (localSettings.Values.ContainsKey("sounds"))
{
clsGlobal.statModel.Sounds = (bool)localSettings.Values["sounds"];
}
And when I play a sound, a simple if condition is included:
if (clsGlobal.statModel.Sounds && clsGlobal.fileInputs.ContainsKey("problem.wav"))
{
var snd1 = clsGlobal.fileInputs["problem.wav"];
if (snd1 != null)
{
snd1.Reset();
snd1.Start();
}
}
I am using the AudioGraph API for my sounds. Works better than the MediaPlayer route since that was giving me a weird situation where the sounds would not play sometimes on the first request. AudioGraph works better for me and is really fast to play sounds.
I'm learning Universal Windows Platform and I'm currently analysing music player using SoundCloud API from this site. Link to the github project is at the very bottom of the page. To get this project to work variable SoundCloudClientId from App.xaml.cs should be filled in (I used client id from previous example on the same page, not sure if I can paste that).
When application starts the NowPlaying page is loaded and changing tracks causes UI to update accordingly. The problem is when I navigate to any other page and return back to NowPlaying. I can still change music using buttons, but UI doesn't change (song title, album title etc.).
Important parts of the code:
NowPlaying.xaml
<ImageBrush x:Name="albumrtImage" ImageSource="Assets\Albumart.png" Stretch="UniformToFill" />
<TextBlock x:Name="txtSongTitle" Grid.Row="0" HorizontalAlignment="Center" Text="Song Title " FontSize="25" Foreground="White" Style="{StaticResource HeaderTextBlockStyle}" TextTrimming="WordEllipsis" />
<TextBlock x:Name="txtAlbumTitle" Grid.Row="0" Text="Label " HorizontalAlignment="Center" FontWeight="Light" FontSize="20" Foreground="#9799a5" Style="{StaticResource BodyTextBlockStyle}" TextTrimming="WordEllipsis"/>
NowPlaying.xaml.cs
async void BackgroundMediaPlayer_MessageReceivedFromBackground(object sender, MediaPlayerDataReceivedEventArgs e)
{
TrackChangedMessage trackChangedMessage;
if (MessageService.TryParseMessage(e.Data, out trackChangedMessage))
{
// When foreground app is active change track based on background message
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var songIndex = GetSongIndexById(trackChangedMessage.TrackId);
if (songIndex >= 0)
{
var song = App.likes[songIndex];
LoadTrack(song); //Update UI
}
});
return;
}
BackgroundAudioTaskStartedMessage backgroundAudioTaskStartedMessage;
if (MessageService.TryParseMessage(e.Data, out backgroundAudioTaskStartedMessage))
{
backgroundAudioTaskStarted.Set();
return;
}
}
private async void LoadTrack(SoundCloudTrack currentTrack)
{
try
{
//Change album art
string albumartImage = Convert.ToString(currentTrack.artwork_url);
if (string.IsNullOrWhiteSpace(albumartImage))
{
albumartImage = #"ms-appx:///Assets/Albumart.png";
}
else
{
albumartImage = albumartImage.Replace("-large", "-t500x500");
}
//Next 3 lines when pages were switched don't cause UI to update
albumrtImage.ImageSource = new BitmapImage(new Uri(albumartImage));
txtSongTitle.Text = currentTrack.title;
txtAlbumTitle.Text = Convert.ToString(currentTrack.user.username);
}
catch (Exception ex)
{
MessageDialog showMessgae = new MessageDialog("Something went wrong. Please try again. Error Details : " + ex.Message);
await showMessgae.ShowAsync();
}
}
After navigating from NowPlaying->Me->NowPlaying and clicking next the track changes, but UI doesn't update as seen on the screen below:
UI problem
I'm trying to reproduce the problem on a simple example, but without any luck. What could cause this issue? Any help is appreciated.
I've found the solution. The problem was with cache. The NowPlaying page required putting following property:
NavigationCacheMode="Required"
I just want to add that you can also check navigation mode in OnNavigatedTo method. It can be helpful when for instance you would like to refresh the data when returning to the selected page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if(e.NavigationMode == NavigationMode.Back)
{
//refresh the data here...
}
}
Hi all,
i need to block the screen using busyindicator. when i click the view (Sample.xaml) the event was written in (Sample.xaml.cs) that leads to execute the code that was written in a separate class named (Sample.cs). So in the separate class file i have the implementation of busyindicator. but it doesn't work.
Sample.xaml:
<UserControl x:Class="Pool.View.CadViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="400">
<Grid>
<toolkit:BusyIndicator IsBusy="{Binding IsBusy}">
<Grid x:Name="CadLayoutRoot" MouseLeftButtonUp="CadLayoutRoot_MouseLeftButtonUp" MouseRightButtonDown="CadLayoutRoot_MouseRightButtonDown" MouseRightButtonUp="CadLayoutRoot_MouseRightButtonUp" MouseLeftButtonDown="CadLayoutRoot_MouseLeftButtonDown" MouseMove="CadLayoutRoot_MouseMove" MouseWheel="CadLayoutRoot_MouseWheel" LostMouseCapture="CadLayoutRoot_LostMouseCapture" >
</Grid>
</toolkit:BusyIndicator>
</Grid>
</UserControl>
Sample.xaml.cs:
private void CadLayoutRoot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
UpdateViewAndViewModel();
}
private void UpdateViewAndViewModel()
{
Sample bfmobj = new BFM(View.CadViewer.radius2, View.CadViewer.model.Blocks,
View.CadViewer.Step, View.CadViewer.StepType, View.CadViewer.StepPosition);
}
Sample.cs:
public class Sample: ViewModelBase
{
public BFM(bool radius, DxfBlockCollection block, string step, string steptype, string stepposition)
{
this.Radius = radius;
this.Block = block;
this.Step = step;
this.StepType = steptype;
this.StepPosition = stepposition;
GetBFMPart();
}
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
if (_isBusy != value)
{
_isBusy = value;
OnPropertyChanged("IsBusy");
}
}
}
public void GetBFMPart()
{
IsBusy = true;
//code sample
IsBusy = false;
}
}
In the Function GetBFMPart i had the code to retrieve the data. So i need to block the Screen till the the data to be retrieved. How to do that? Please Help me to fix this issue..
Well, for a start, you're creating your Sample object but not setting it as the DataContext for your view. Either modify the existing ViewModel/DataContext or replace it with the new Sample object you've created.
private void UpdateViewAndViewModel()
{
Sample bfmobj = new BFM(View.CadViewer.radius2, View.CadViewer.model.Blocks,
View.CadViewer.Step, View.CadViewer.StepType, View.CadViewer.StepPosition);
this.DataContext = bfmobj;
}
However, I think you may also run into issues to do with threading, but I'm not very familiar with Silverlight.
In standard WPF, the framework is waiting for your function to complete before it can update the UI, as your function is running on the UI thread. And since your function effectively doesn't change the value of that bool (it does, but because nothing else can execute before it's finished, the value remains unchanged), the UI stays the same.
You need to run your code that "does the work" on a separate thread, the easiest way to do this is to use a task:
public void GetBFMPart()
{
IsBusy = true;
var task = Task.Factory.StartNew(delegate
{
// do your work here then set IsBusy = false
for (int i = 0; i < 5; i++)
{
System.Threading.Thread.Sleep(1000);
}
IsBusy = false;
});
}
I found Two Way and UpdateSourceTrigger to work
<toolkit:BusyIndicator HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
BusyContent="{Binding BusyMessage,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
IsBusy="{Binding UserControlIsBusy,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
d:IsHidden="True" />