Programatically create a Popup with a Child element initialised by Caliburn Micro - c#

Windows.Media.Captures has a handy CameraCaptureUI class that can be instantiated as follows to show a dialog to the user to capture photos or videos:
// Create dialog to Capture Video
CameraCaptureUI dialog = new CameraCaptureUI();
dialog.VideoSettings.Format = CameraCaptureUIVideoFormat.Mp4;
StorageFile file = await dialog.CaptureFileAsync(CameraCaptureUIMode.Video);
if (file != null)
{
// Do something with file...
}
I would like to create my own custom audio capture class that works in a very similar way:
// Create dialog to Capture Audio
AudioCaptureUI dialog = new AudioCaptureUI();
StorageFile file = await dialog.CaptureFileAsync();
if (file != null)
{
// Do something with file...
}
To do the above, I created the following three files:
AudioCaptureUI - The class that a user instantiates to show the audio capture dialog
AudioCaptureView - UI View for the audio capture experience
AudioCaptureViewModel - ViewModel that contains all the audio capture logic
To create a full screen audio capture dialog, I have figured out that the best way is to use a Popup and set its child to the AudioCaptureView. The problem I have with this approach is that it is pushing me use a View-First pattern. Since I am using Caliburn Micro, I wanted to be able to use CM to instantiate a View by creating the ViewModel first.
What I currently have is something on the following lines:
public class AudioCaptureUI
{
private Popup _popup;
private TaskCompletionSource<StorageFile> _taskCompletionSource;
public IAsyncOperation<StorageFile> CaptureFileAsync()
{
// Force my View to be full screen
AudioCaptureView audioCaptureView = new AudioCaptureView
{
Width = Window.Current.Bounds.Width,
Height = Window.Current.Bounds.Height
};
// Creating View, instead of a ViewModel. Renders Caliburn Micro useless!
_popup = new Popup { Child = audioCaptureView };
if (_popup.Child != null)
{
SubscribeEvents();
_popup.IsOpen = true;
}
return AsyncInfo.Run(WaitForInput);
}
...
}
The above pattern works. However, I am forced to wire all my actions manually and cannot leverage Caliburn Micro's MVVM goodness.
How else should I instantiate a ViewModel programatically from my AudioCaptureUI class?
It is also important to highlight that I am working on a Windows Store app and using the WinRT CM port.

You could always port the WindowManager to WinRT in your own project. Looking at the source I don't think too much will need to change. https://caliburnmicro.codeplex.com/SourceControl/latest#src/Caliburn.Micro.Platform/net40/WindowManager.cs
You could bring over the Interface as well and use DI but for the sake of time here is the stand alone class. The main part for Model first binding is the ViewLocator.LocateForModel which returns the View from the ViewModel (aka the magic)
using System;
using System.Collections.Generic;
using Windows.UI.Xaml.Controls.Primitives;
namespace Caliburn.Micro
{
public class WindowManager
{
public virtual void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null)
{
var popup = CreatePopup(rootModel, settings);
var view = ViewLocator.LocateForModel(rootModel, popup, context);
popup.Child = view;
//popup.SetValue(View.IsGeneratedProperty, true);
ViewModelBinder.Bind(rootModel, popup, null);
Caliburn.Micro.Action.SetTargetWithoutContext(view, rootModel);
var activatable = rootModel as IActivate;
if (activatable != null)
{
activatable.Activate();
}
var deactivator = rootModel as IDeactivate;
if (deactivator != null)
{
popup.Closed += delegate { deactivator.Deactivate(true); };
}
popup.IsOpen = true;
//popup.CaptureMouse();
}
protected virtual Popup CreatePopup(object rootModel, IDictionary<string, object> settings)
{
var popup = new Popup();
ApplySettings(popup, settings);
return popup;
}
bool ApplySettings(object target, IEnumerable<KeyValuePair<string, object>> settings)
{
if (settings != null)
{
var type = target.GetType();
foreach (var pair in settings)
{
var propertyInfo = type.GetPropertyCaseInsensitive(pair.Key);
if (propertyInfo != null)
{
propertyInfo.SetValue(target, pair.Value, null);
}
}
return true;
}
return false;
}
}
}
Then all you need to do is create an instance and give it a ViewModel:
var windowManager = new WindowManager();
windowManager.ShowPopup(new MyPopupThingViewModel());
Note: I've only used this in an 8.1 app so not 100% sure if it will completely work with 8.0

You might have some success with the Caliburn.Micro WindowManager. There isn't a great deal about it in the official documentation (you're best off searching Google and the CM discussions). I've used it in one of my applications where I needed to host a particular ViewModel in a new window, and wanted to utilise all of the Caliburn.Micro goodness (and my existing Views).
Have a look at the Caliburn.Micro.IWindowManager interface, you'll see some handy methods that you can call from a WindowManager instance (depending on the popup type you're after).
public interface IWindowManager
{
bool? ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null);
void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null);
void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null);
}
In my application, to pop up my a Window with my ViewModel of choice, I did something along these lines (your names inserted):
// Some basic Window settings.
dynamic settings = new ExpandoObject();
settings.Title = "Test Window";
settings.WindowStartupLocation = WindowStartupLocation.Manual;
settings.SizeToContent = SizeToContent.Manual;
settings.Width = 450;
settings.Height = 300;
var localAudioCaptureViewModel new AudioCaptureViewModel ();
WindowManagerFactory.WindowManager.ShowWindow(localAudioCaptureViewModel, null, settings); // I didn't require context (null)
Caliburn.Micro should resolve your Views to the correct ViewModels, and you're good to go.

Related

Xamarin Forms Custom SearchbarRenderer with Loadingcircle like Netflix

I really want to implement a loading circle in the searchbar (overriding the cancel button while loading) on android like Netflix.
Currently Im getting the cancel button with
var searchBttId = searchView.Resources.GetIdentifier("android:id/search_close_btn", null, null);
var imbCancel = (searchView.FindViewById(searchBttId) as Android.Widget.ImageView)
but I dont know how to add a progresscircle and how to interact with it. I thought of adding it with AddView, but nothing pops up when testing.
Test:
var _v = new Android.Widget.ImageView(Context) { };
_v.SetBackgroundColor(new Android.Graphics.Color(200, 0, 0));
var c = new LayoutParams(500, 500);
_v.LayoutParameters = c;
searchView.AddView(_v);
I really want to implement a loading circle in the searchbar (overriding the cancel button while loading) on android like Netflix.
According to your description, you want to override searchbar's search_close_btn using other icon, like loading circle? Am I right?
If yes, I suggest you can use custom render to change searchbar's search_close_btn image.
Firstly, create custom Searchbar that inherit SearchBar in Form.cs.
public class MySearchBar:SearchBar
{
}
Then implementing custom render in Android platform.
[assembly: ExportRenderer(typeof(MySearchBar), typeof(MySearchBarRenderer))]
namespace demo3.Droid
{
public class MySearchBarRenderer: SearchBarRenderer
{
public MySearchBarRenderer(Context context):base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var searchView = Control;
int searchViewCloseButtonId = Control.Resources.GetIdentifier("android:id/search_close_btn", null, null);
var closeIcon = searchView.FindViewById(searchViewCloseButtonId);
(closeIcon as ImageView).SetImageResource(Resource.Drawable.plu3);
}
}
}
}
Please see the gif:
About Custom Render, you can take a look:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/entry

Update WPF with externalevent and MVVM schema

I'm currently developping a new soft which analyze a lot of data on Revit.
I have my WPF View, my ViewModel and a long methode.
init.cs:
IExternalEventHandler handlerEvent = new FamilyAnalysis();
var familyAnalysisEvent = ExternalEvent.Create(handlerEvent);
Gui = new AnalysisView(familyAnalysisEvent);
Gui.Show();
AnalysisView.xaml.cs:
public AnalysisView(ExternalEvent externalEvent)
{
InitializeComponent();
Windows.SetLocation(this);
Language = System.Windows.Markup.XmlLanguage.GetLanguage(System.Threading.Thread.CurrentThread.CurrentCulture.Name);
DataContext = new AnalysisViewModel(externalEvent, this);
}
AnalysisViewModel.cs:
public ICommand StartAnalysisCommand
{
get
{
return _startAnalysisCommand ?? (_startAnalysisCommand = new RelayCommand(() =>
{
//Some check before then..
_externalEvent.Raise();
}));
}
}
during the _externalEvent.Raise() I would like to have a loading form updating the state of the methode.
I tried to search about dispatcher but I'm not sure how to do that and all my try didn't worked. The loading screen pop but the value are not getting updated.
Any advice ?

How to call a variable from .droid to .shared project in VS Xamarin?

this may be a very basic question but I'd like to ask if how can I output the variable from a .droid to a .shared project in VS Xamarin. Let's say I have a variable with value in a renderer from a .droid, then I'd like to call it from a .shared page. How can I do this?
ScannerPageRenderer.cs (From .droid)
try
{
var BarcodeText = result.text.ToString();
var BarcodeType = result.typeText.ToString();
}
If you have a ScannerPageRenderer then this means that you are using a custom Renderer for a Xamarin.Forms page.
To pass back the values you can either create public properties in the Page, for this example let's call it ScannerPage then pass the values in the Element object of the custom renderer as this is the Page you are attaching the custom renderer.
ex
private void Scan()
{
//or however you are getting the result...
var result = getScanResult();
var scanPage = Element as ScannerPage;
if (scanPage == null)
{
return;
}
scanPage.BarcodeText = result.text.ToString();
scanPage.BarcodeType = result.typeText.ToString();
}
Another way would be that instead of the two properties you create a public method where you pass in the result object the same way as above with the properties. In such method you will then assign your properties or fields with the values.
private void Scan()
{
var result = getScanResult();
var scanPage = Element as ScannerPage;
if (scanPage == null)
{
return;
}
scanPage.OnScanCompleted(result);
}
Hope this helps.-

Binding a HTML control into a stacklayout using xamarin forms

Being a newbie to Xamrin I am struggling to adding some HTML to a StackLayout via Xamarin Forms. I have tried quite a few things and had a Google around.
Firstly I can't work out which bindable object I am supposed to be using. As I cannot find a straight answer on Google/Xamarin I am going to assume this is not as easy I was hoping.
var nameEntry = new Label ();
nameEntry.SetBinding (Label.TextProperty, "Club.ClubName");
var webView = new WebView ();
webView.SetBinding ( ??? , "Club.Description");
var content = new StackLayout {
Children = {
nameEntry,
???
}
};
I am not sure if this is possible within Xamarin forms itself. Can anyone help?
I should point out my data for the form is being retrieved asynchronously on a remote json endpoint
protected override void OnAppearing ()
{
base.OnAppearing ();
if (ViewModel == null || ViewModel.IsLoading)
return;
ViewModel.LoadItemsCommand.Execute (Club.ClubId);
}
My remote json api contains, Description contatins a HTML snippet which I would like to use.
{
ClubName: "Stourbridge",
Description: "<p>This club meets every TWO weeks on a <b>Friday</b>.</p>"
...
}
Try the following example that will show how to do the bindings.
Note that you have to use a HtmlWebViewSource to achieve this, and bind the WebView.Source to this.
Clicking the button will change the view model and update the WebView appropriately to the newly changed text.
StackLayout objStackLayout = new StackLayout();
MyView objMyView = new MyView();
objMyView.MyHtml = "<html><head></head><body><h1>Title</h1><p>Some body text</p></body></html>";
HtmlWebViewSource objHtmlWebViewSource = new HtmlWebViewSource();
objHtmlWebViewSource.SetBinding(HtmlWebViewSource.HtmlProperty, "MyHtml");
objHtmlWebViewSource.BindingContext = objMyView;
WebView objWebview = new WebView();
objWebview.HorizontalOptions = LayoutOptions.FillAndExpand;
objWebview.VerticalOptions = LayoutOptions.FillAndExpand;
objWebview.Source = objHtmlWebViewSource;
Button objMyButton2 = new Button();
objMyButton2.Text="Change Html";
objMyButton2.Clicked+=((o2,e2)=>
{
objMyView.MyHtml = "<html><head></head><body><h1>Title</h1><p>Some body text that has changed.</p></body></html>";
});
objStackLayout.Children.Add(objMyButton2);
objStackLayout.Children.Add(objWebview);
The view model is just a simple one with a bindable property as below:-
public class MyView
: Xamarin.Forms.View
{
public static readonly BindableProperty MyHtmlProperty = BindableProperty.Create<MyView, string>(p => p.MyHtml, default(string));
public string MyHtml
{
get { return (string)GetValue(MyHtmlProperty); }
set { SetValue(MyHtmlProperty, value); }
}
}
Before clicking the button gives:-
After clicking the button, adjusts the view model, and automatically updates the control via the binding giving:-

Telerik PdfViewer - Binding to a PDF source

I've tried the samples on telerik's website as well, and to no avail. So I have this code:
public ICommand EmailPopUpCmd { get; set; }
private void EmailPopUp(object sender) {
//ToDo: pdf viewer pop up
selectedDataRow = (DataRow)sender;
var window = new Window();
window.Content = new EmailView() { DataContext = this}; //shares the same data context
MemoryStream str = new MemoryStream();//= new MemoryStream(pdfAsByteArray);//new System.Uri(#"pack://application:,,,/Resources/TestPDF.pdf", System.UriKind.RelativeOrAbsolute);
using(FileStream fs = File.OpenRead(#"C:\Source\UI.MailViewer\Resources\TestPDF.pdf")) {
str.SetLength(fs.Length);
fs.Read(str.GetBuffer(),0, (int)fs.Length);
}
AttachmentPath = new PdfDocumentSource(str);
if (window.ShowDialog() == true) {
//from child back to parent
}
}
C:\Source\UI.MailViewer\Resources\TestPDF.pdf
Is where my PDF is located and I bind it to the UI as follow:
telerik:RadPdfViewer x:Name="pdfEmailViewer"
Grid.Row="2"
DocumentSource="{Binding AttachmentPath,Converter={StaticResource DebugConverter}}">
Now using this, I get an object not set to an instance error. Any idea as to why? Using the debug converter, the value of the PDF document created is null, why is that happening?
Okay so to help other users, I found this on the telerik forums: (Telerik Forum Link)
There is one additional thing to do in order to make RadPdfViewer work in a WinForms application: you should ensure that the Application.Current property is initialized before the user control is initialized, for example by adding this code in the constructor of the user control:
public PdfViewerControl() {
if (Application.Current == null)
new Application();
InitializeComponent();
}
This worked for me in WPF as well.

Categories

Resources