C# Prism Сommunication between AvalonDock and MainApplicationView - prism

I'm trying to figure out how to work with AvalonDock in Prism. My MainApplciationView Xaml code:
<Window x:Class="WPFTestLab.Views.ApplicationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fa="http://schemas.fontawesome.com/icons/"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:core="clr-namespace:WPFTestLab.Core;assembly=WPFTestLab.Core"
xmlns:avalonDock="https://github.com/Dirkster99/AvalonDock"
Title="{Binding Title}" Width="1280" Height="720" WindowStartupLocation="CenterScreen">
<Grid>
<avalonDock:DockingManager prism:RegionManager.RegionName="{x:Static core:RegionNames.MainRegion}">
<avalonDock:LayoutRoot>
<avalonDock:LayoutPanel Orientation="Horizontal">
<avalonDock:LayoutDocumentPaneGroup DockWidth="100" Orientation="Vertical">
<avalonDock:LayoutDocumentPane>
</avalonDock:LayoutDocumentPane>
</avalonDock:LayoutDocumentPaneGroup>
</avalonDock:LayoutPanel>
</avalonDock:LayoutRoot>
</avalonDock:DockingManager>
</Grid>
And I create docking manager region adapter:
public class AvalonDockingRegionAdapter : RegionAdapterBase<DockingManager>
{
private bool _updatingActiveViewsInManagerActiveContentChanged;
#region Constructor
public AvalonDockingRegionAdapter(IRegionBehaviorFactory factory)
: base(factory)
{
_updatingActiveViewsInManagerActiveContentChanged = false;
}
#endregion //Constructor
#region Overrides
protected override IRegion CreateRegion()
{
return new SingleActiveRegion();
}
protected override void Adapt(IRegion region, DockingManager regionTarget)
{
regionTarget.ActiveContentChanged += delegate (
object sender, EventArgs e)
{
this.ManagerActiveContentChanged(sender, e, region, regionTarget);
};
region.Views.CollectionChanged += delegate (
Object sender, NotifyCollectionChangedEventArgs e)
{
this.OnViewsCollectionChanged(sender, e, region, regionTarget);
};
region.Views.CollectionChanged += delegate (
Object sender, NotifyCollectionChangedEventArgs e)
{
this.OnActiveViewsCollectionChanged(sender, e, region, regionTarget);
};
regionTarget.DocumentClosed += delegate (
Object sender, DocumentClosedEventArgs e)
{
this.OnDocumentClosedEventArgs(sender, e, region);
};
}
private void OnActiveViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, IRegion region, DockingManager regionTarget)
{
if (_updatingActiveViewsInManagerActiveContentChanged) return;
if (e.Action == NotifyCollectionChangedAction.Add)
{
if (regionTarget.ActiveContent != null && regionTarget.ActiveContent != e.NewItems[0] &&
region.ActiveViews.Contains(regionTarget.ActiveContent))
region.Deactivate(regionTarget.ActiveContent);
regionTarget.ActiveContent = e.NewItems[0];
}
else if (e.Action == NotifyCollectionChangedAction.Remove &&
e.OldItems.Contains(regionTarget.ActiveContent))
{
regionTarget.ActiveContent = null;
}
}
private void ManagerActiveContentChanged(object sender, EventArgs e, IRegion region, DockingManager regionTarget)
{
try
{
_updatingActiveViewsInManagerActiveContentChanged = true;
if (regionTarget == sender)
{
var activeContent = regionTarget.ActiveContent;
if (activeContent != null)
{
foreach (var item in region.ActiveViews.Where(it => it != activeContent))
if (region.Views.Contains(item))
region.Deactivate(item);
if (region.Views.Contains(activeContent) && !region.ActiveViews.Contains(activeContent))
region.Activate(activeContent);
}
}
}
finally
{
_updatingActiveViewsInManagerActiveContentChanged = false;
}
}
#endregion //Overrides
#region Event Handlers
/// <summary>
/// Handles the NotifyCollectionChangedEventArgs event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event.</param>
/// <param name="region">The region.</param>
/// <param name="regionTarget">The region target.</param>
void OnViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, IRegion region, DockingManager regionTarget)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (FrameworkElement item in e.NewItems)
{
UIElement view = item as UIElement;
if (view != null)
{
//Create a new layout document to be included in the LayoutDocuemntPane (defined in xaml)
LayoutDocument newLayoutDocument = new LayoutDocument();
newLayoutDocument.Content = item;
PaneViewModel viewModel = (PaneViewModel)item.DataContext;
if (viewModel != null)
newLayoutDocument.Title = viewModel.Title;
//Store all LayoutDocuments already pertaining to the LayoutDocumentPane (defined in xaml)
List<LayoutDocument> oldLayoutDocuments = new List<LayoutDocument>();
//Get the current ILayoutDocumentPane ... Depending on the arrangement of the views this can be either
//a simple LayoutDocumentPane or a LayoutDocumentPaneGroup
ILayoutDocumentPane currentILayoutDocumentPane = (ILayoutDocumentPane)regionTarget.Layout.RootPanel.Children[0];
if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPaneGroup))
{
//If the current ILayoutDocumentPane turns out to be a group
//Get the children (LayoutDocuments) of the first pane
LayoutDocumentPane oldLayoutDocumentPane = (LayoutDocumentPane)currentILayoutDocumentPane.Children.ToList()[0];
foreach (LayoutDocument child in oldLayoutDocumentPane.Children)
{
oldLayoutDocuments.Insert(0, child);
}
}
else if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPane))
{
//If the current ILayoutDocumentPane turns out to be a simple pane
//Get the children (LayoutDocuments) of the single existing pane.
foreach (LayoutDocument child in currentILayoutDocumentPane.Children)
{
oldLayoutDocuments.Insert(0, child);
}
}
//Create a new LayoutDocumentPane and inserts your new LayoutDocument
LayoutDocumentPane newLayoutDocumentPane = new LayoutDocumentPane();
newLayoutDocumentPane.InsertChildAt(0, newLayoutDocument);
//Append to the new LayoutDocumentPane the old LayoutDocuments
foreach (LayoutDocument doc in oldLayoutDocuments)
{
newLayoutDocumentPane.InsertChildAt(0, doc);
}
//Traverse the visual tree of the xaml and replace the LayoutDocumentPane (or LayoutDocumentPaneGroup) in xaml
//with your new LayoutDocumentPane (or LayoutDocumentPaneGroup)
if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPane))
regionTarget.Layout.RootPanel.ReplaceChildAt(0, newLayoutDocumentPane);
else if (currentILayoutDocumentPane.GetType() == typeof(LayoutDocumentPaneGroup))
{
currentILayoutDocumentPane.ReplaceChild(currentILayoutDocumentPane.Children.ToList()[0], newLayoutDocumentPane);
regionTarget.Layout.RootPanel.ReplaceChildAt(0, currentILayoutDocumentPane);
}
newLayoutDocument.IsActive = true;
}
}
}
}
/// <summary>
/// Handles the DocumentClosedEventArgs event raised by the DockingNanager when
/// one of the LayoutContent it hosts is closed.
/// </summary>
/// <param name="sender">The sender</param>
/// <param name="e">The event.</param>
/// <param name="region">The region.</param>
void OnDocumentClosedEventArgs(object sender, DocumentClosedEventArgs e, IRegion region)
{
region.Remove(e.Document.Content);
}
#endregion
}
My question is how I can manipulate docking manager from MainApplicationViewModel. For example, I want to catch the active document content change and disable button in MainApplicationView. Or clicking the button in MainApplicationView should change the text in current active document view.

Hopefully, the DockingManager has properties and commands that you can bind to stuff on your ApplicationWindowViewModel.
If not, you have to resort to EventToCommand and attached behaviours.
From there on, everything's standard. Document get's active, document view model's IsActive property becomes true, a service is notified, another view model listening to the service becomes aware of the change, the text is changed...

Related

How to set picker row item long text in two lines using custom renderer in xamarin forms android?

I want to show long text of picker item in two lines using custom render how can I achieve it?
First , you need a custom renderer for picker .
To override the item you need to replace the original view with a AlertDialog .
Then custom a ListView and set it as AlertDialog.View .
Then you can customize everything in the Adapter(here we need to customize the textview ).
Sample code
[assembly: ExportRenderer(typeof(Picker), typeof(MyPickerRenderer))]
namespace FormsApp.Droid
{
public class MyAdapter : ArrayAdapter
{
private IList<string> _data;
Context _context;
public MyAdapter(Context context, int resource , IList<string> data) : base(context,resource)
{
_context = context;
_data = data;
}
public override int Count => _data.Count;
public override Android.Views.View GetView(int position, Android.Views.View convertView, ViewGroup parent)
{
TextView textview = new TextView(_context);
textview.TextSize = 18;
textview.SetTextColor(Android.Graphics.Color.DarkGray);
textview.Ellipsize = TruncateAt.End;
textview.SetMaxLines(2); //this line
textview.Text = _data[position];
return textview;
}
}
public class MyListView : Android.Widget.ListView
{
public MyListView(Context context, IList<string> data) : base(context)
{
this.DividerHeight = 0;
this.Adapter = new MyAdapter(context, 0, data);
}
}
class MyPickerRenderer : Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
{
IElementController ElementController => Element as IElementController;
public MyPickerRenderer(Context context):base(context)
{
}
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new MyListView(Context, model.Items);
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.SetPadding(35, 30, 35, 0);
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("Cancel ", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
}
}
}
Testing code in Forms xaml
<Picker x:Name="picker" Title="Select a monkey">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>abc</x:String>
<x:String>aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbccccccccccccccccdddddddddddddddddddddd</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
Before
After

How to set corner radius for button in xamarin uwp by programatically?

I am having an grid holding in Button. I want to set corner radius for this button by programmatically in xamarin.uwp. If it possible means i want to set corner radius for Bottom left alone. Please suggest your ideas for this query.
For your requirement, you could custom BottomLeft BindableProperty. then use it in your custom button render like the following.
Custom Forms buttom
public class MyButton : Button
{
public static readonly BindableProperty BottomLeftProperty = BindableProperty.Create(
propertyName: "BottomLeft",
returnType: typeof(int),
declaringType: typeof(MyButton),
defaultValue: default(int));
public int BottomLeft
{
get { return (int)GetValue(BottomLeftProperty); }
set { SetValue(BottomLeftProperty, value); }
}
}
CustomButtonRenderer.cs
public class CustomButtonRenderer : ButtonRenderer
{
Windows.UI.Xaml.Controls.ContentPresenter _contentPresenter;
MyButton _myElement;
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (Control == null)
{
SetNativeControl(new FormsButton());
}
if (e.NewElement != null)
{
Control.Loaded += Control_Loaded;
_myElement = Element as MyButton;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == MyButton.BottomLeftProperty.PropertyName)
{
UpdateBottomLeftBorderRadius(_myElement.BottomLeft);
}
}
private void Control_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
_contentPresenter = MyFindChildByName(Control, "ContentPresenter") as Windows.UI.Xaml.Controls.ContentPresenter;
if (_myElement.IsSet(MyButton.BottomLeftProperty) && _myElement.BottomLeft != (int)MyButton.BottomLeftProperty.DefaultValue)
{
UpdateBottomLeftBorderRadius(_myElement.BottomLeft);
}
}
private void UpdateBottomLeftBorderRadius(int bottomLeft)
{
if (_contentPresenter != null)
{
_contentPresenter.CornerRadius = new CornerRadius(0, 0, 0, bottomLeft);
}
}
public static DependencyObject MyFindChildByName(DependencyObject parant, string ControlName)
{
int count = VisualTreeHelper.GetChildrenCount(parant);
for (int i = 0; i < count; i++)
{
var MyChild = VisualTreeHelper.GetChild(parant, i);
if (MyChild is FrameworkElement && ((FrameworkElement)MyChild).Name == ControlName)
return MyChild;
var FindResult = MyFindChildByName(MyChild, ControlName);
if (FindResult != null)
return FindResult;
}
return null;
}
}
Usage
<local:MyButton x:Name="Hello" Text="hello"
WidthRequest="100"
HeightRequest="100"
Margin="0,100,0,0"
BottomLeft="15"
VerticalOptions="Center"
HorizontalOptions="Center"
Clicked="MyButton_Clicked"/>
For edit the bottom left property programatically.
HelloBtn.BottomLeft = 30;

Kentico 10 Filtered Repeater with multiple filters not working properly

I have a data source, filter, and a filtered repeater that I have created custom filters for.
For some reason, both my filters work without the other on the screen. One works fine with the other filter on the screen.
The last filter refuses to work when I have the other filter on the screen but works fine without anything else.
I am pretty sure it has something to do with my code behind files which I will put below.
FILTER#1
using CMS.DocumentEngine;
using CMS.Helpers;
using System;
using System.Web;
using System.Web.UI.WebControls;
using CMS.DocumentEngine.Web.UI;
public partial class CMSGlobalFiles_SectorFilterControl : CMSAbstractDataFilterControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
/// <summary>
/// Sets up the inner child controls.
/// </summary>
private void SetupControl()
{
// Hides the filter if StopProcessing is enabled
if (this.StopProcessing)
{
this.Visible = false;
}
// Initializes only if the current request is NOT a postback
else if (!RequestHelper.IsPostBack())
{
// Loads product departments as filtering options
InitializeClientSectors();
}
}
/// <summary>
/// Loads all existing product departments as filtering options into the department drop-down list.
/// </summary>
private void InitializeClientSectors()
{
// Adds the default '(all)' option
this.drpSector.Items.Insert(0, new ListItem("(all)", "##ALL##"));
var clientSectors = DocumentHelper.GetDocuments("BBUS.Sector")
.Path("/Sector/", PathTypeEnum.Children)
.OnSite("Balfour-dev.allata.com");
if (!DataHelper.DataSourceIsEmpty(clientSectors))
{
int count = 1;
foreach (var clientSector in clientSectors)
{
var ClientSectorID = clientSector.GetValue("SectorID").ToString();
this.drpSector.Items.Insert(count++, new ListItem(clientSector.DocumentName, ClientSectorID));
}
}
}
/// <summary>
/// Generates a WHERE condition and ORDER BY clause based on the current filtering selection.
/// </summary>
private void SetFilter()
{
string where = null;
// Generates a WHERE condition based on the selected product department
if (this.drpSector.SelectedIndex > 0 && this.drpSector.SelectedValue != null)
{
//where = string.Format("clientSector = {0}", this.drpClientSector.SelectedValue);
where = string.Format(
"sector LIKE '%|{0}|%' " +
"OR sector LIKE '{0}|%' " +
"OR sector LIKE '%|{0}' " +
"OR sector = '{0}'", this.drpSector.SelectedValue);
}
if (where != null)
{
// Sets the Where condition
this.WhereCondition = where;
}
// Raises the filter changed event
this.RaiseOnFilterChanged();
}
/// <summary>
/// Init event handler.
/// </summary>
protected override void OnInit(EventArgs e)
{
// Creates the child controls
SetupControl();
base.OnInit(e);
}
/// <summary>
/// PreRender event handler
/// </summary>
protected override void OnPreRender(EventArgs e)
{
var ClientSectorID = HttpContext.Current.Request.QueryString.Get("SectorID");
// Checks if the current request is a postback
if (RequestHelper.IsPostBack())
{
// Applies the filter to the displayed data
SetFilter();
}
else if (!string.IsNullOrEmpty(ClientSectorID))
{
this.drpSector.SelectedIndex = this.drpSector.Items.IndexOf(this.drpSector.Items.FindByValue(ClientSectorID));
SetFilter();
}
base.OnPreRender(e);
}
protected void btnFilter_Click(object sender, EventArgs e)
{
// Remove Query Strings
string url = Request.RawUrl.Split(new[] { '?' })[0];
// Add ClientSectorID Query String
string updatedQueryString = "?" + "SectorID=" + this.drpSector.SelectedValue;
Response.Redirect(url + updatedQueryString);
}
}
FILTER#2
using CMS.DocumentEngine;
using CMS.Helpers;
using System;
using System.Web;
using System.Web.UI.WebControls;
using CMS.DocumentEngine.Web.UI;
public partial class CMSGlobalFiles_SectorFilterControl : CMSAbstractDataFilterControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
/// <summary>
/// Sets up the inner child controls.
/// </summary>
private void SetupControl()
{
// Hides the filter if StopProcessing is enabled
if (this.StopProcessing)
{
this.Visible = false;
}
// Initializes only if the current request is NOT a postback
else if (!RequestHelper.IsPostBack())
{
// Loads product departments as filtering options
InitializeClientSectors();
}
}
/// <summary>
/// Loads all existing product departments as filtering options into the department drop-down list.
/// </summary>
private void InitializeClientSectors()
{
// Adds the default '(all)' option
this.drpSector.Items.Insert(0, new ListItem("(all)", "##ALL##"));
var clientSectors = DocumentHelper.GetDocuments("BBUS.Sector")
.Path("/Sector/", PathTypeEnum.Children)
.OnSite("Balfour-dev.allata.com");
if (!DataHelper.DataSourceIsEmpty(clientSectors))
{
int count = 1;
foreach (var clientSector in clientSectors)
{
var ClientSectorID = clientSector.GetValue("SectorID").ToString();
this.drpSector.Items.Insert(count++, new ListItem(clientSector.DocumentName, ClientSectorID));
}
}
}
/// <summary>
/// Generates a WHERE condition and ORDER BY clause based on the current filtering selection.
/// </summary>
private void SetFilter()
{
string where = null;
// Generates a WHERE condition based on the selected product department
if (this.drpSector.SelectedIndex > 0 && this.drpSector.SelectedValue != null)
{
//where = string.Format("clientSector = {0}", this.drpClientSector.SelectedValue);
where = string.Format(
"sector LIKE '%|{0}|%' " +
"OR sector LIKE '{0}|%' " +
"OR sector LIKE '%|{0}' " +
"OR sector = '{0}'", this.drpSector.SelectedValue);
}
if (where != null)
{
// Sets the Where condition
this.WhereCondition = where;
}
// Raises the filter changed event
this.RaiseOnFilterChanged();
}
/// <summary>
/// Init event handler.
/// </summary>
protected override void OnInit(EventArgs e)
{
// Creates the child controls
SetupControl();
base.OnInit(e);
}
/// <summary>
/// PreRender event handler
/// </summary>
protected override void OnPreRender(EventArgs e)
{
var ClientSectorID = HttpContext.Current.Request.QueryString.Get("SectorID");
// Checks if the current request is a postback
if (RequestHelper.IsPostBack())
{
// Applies the filter to the displayed data
SetFilter();
}
else if (!string.IsNullOrEmpty(ClientSectorID))
{
this.drpSector.SelectedIndex = this.drpSector.Items.IndexOf(this.drpSector.Items.FindByValue(ClientSectorID));
SetFilter();
}
base.OnPreRender(e);
}
protected void btnFilter_Click(object sender, EventArgs e)
{
// Remove Query Strings
string url = Request.RawUrl.Split(new[] { '?' })[0];
// Add ClientSectorID Query String
string updatedQueryString = "?" + "SectorID=" + this.drpSector.SelectedValue;
Response.Redirect(url + updatedQueryString);
}
}
Sadly this is a little bit of a limitation, you can only have 1 filter per Repeater/Data Source (except Smart Search it seems, which can handle multiple).
You will most likely need to combine both of your filters into 1 filter, and combine the logics into one where condition.
https://docs.kentico.com/k10/developing-websites/loading-and-displaying-data-on-websites/filtering-and-paging-data
Would be great to allow multiple filters though!

Is it possible to have one Activity indicator for entire app?

I have activity indicators throughout my app but I find this repetitive. I would like to know if I can define a single activity indicator and use it throughout the app?
Below is code of one of five activity indicators that I have in the app. Everyone of them are exactly the same except for the label text changing.
<AbsoluteLayout x:Name="ActivityInd" IsVisible="False" BackgroundColor="Black" Opacity=".75" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">
<StackLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1" VerticalOptions="Center" HorizontalOptions="Center">
<ActivityIndicator x:Name="Activityblocker" Color="White"/>
<Label Text="Processing Request" TextColor="White"/>
</StackLayout>
</AbsoluteLayout>
If it is possible, where would be best to place so it can be called from anywhere in the app?
EDIT:
Got everything working and replaced all the activity indicators with the control I created. Below is the code, hopefully this can help someone in the future.
Control xaml with bindings
<?xml version="1.0" encoding="UTF-8"?>
<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Myapp.ActivityBlocker"
x:Name="ActivityIndAL" IsVisible="{Binding IsBusy, Source={x:Reference ActivityIndAL}}" BackgroundColor="Black" Opacity=".75" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">
<StackLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1" VerticalOptions="Center" HorizontalOptions="Center">
<ActivityIndicator x:Name="Activityblocker" Color="White" IsEnabled="{Binding IsBusy, Source={x:Reference ActivityIndAL}}" IsRunning="{Binding IsBusy, Source={x:Reference ActivityIndAL}}"/>
<Label Text="{Binding Text, Source={x:Reference ActivityIndAL}}" x:Name="ActivityIndLabel" TextColor="White"/>
</StackLayout>
</AbsoluteLayout>
Control xaml.cs
public partial class ActivityBlocker : AbsoluteLayout
{
public ActivityBlocker()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(ActivityBlocker));
public static readonly BindableProperty Running = BindableProperty.Create(nameof(IsBusy), typeof(bool), typeof(ActivityBlocker), false);
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
public bool IsBusy
{
get
{
return (bool)GetValue(Running);
}
set
{
SetValue(Running, value);
}
}
}
How to use the control: You will add xmlns:control="clr-namespace:MyApp" to the contentpage of where you want to use the control. Now add the control and the text. Note: IsBusy="False" is not required because it's default value is false in BindableProperty.Create. IsBusy can be accessed and changed from backend code by doing this indicator.IsBusy = true;
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:control="clr-namespace:MyApp"
....
<ContentPage.Content>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<control:ActivityBlocker x:Name="indicator" Text="Processing Request" IsBusy="False"/>
</AbsoluteLayout>
</ContentPage.Content>
....
Is this covering the whole page? if that is so you can use a DependencyService to Show/Hide a loading indicator You will have to download this NuGet packages for Android/iOS: AndHUD/BTProgressHUD like this:
using XXXX.Helpers;
namespace XXXX
{
public interface IHudService
{
/// <summary>
/// Shows hud in the secreen
/// </summary>
/// <param name="ProgressText">Set progress</param>
void ShowHud(string ProgressText = StaticData.Loading);
/// <summary>
/// Hides hud.
/// </summary>
void HideHud();
/// <summary>
/// Set text.
/// </summary>
/// <param name="Text">Set text to hub.</param>
void SetText(string Text);
/// <summary>
/// Set progress.
/// </summary>
/// <param name="Progress">Set progress.</param>
/// <param name="ProgressText">Set Text.</param>
void SetProgress(double Progress, string ProgressText = "");
}
}
Android:
using AndroidHUD;
using Android.Views;
using Xamarin.Forms;
using XXXX.Droid;
using XXXX.DependencyServices;
using XXXX.Helpers;
[assembly: Dependency(typeof(DroidHudService))]
namespace XXXX.Droid
{
public class DroidHudService : IHudService
{
#region IHudManager implementation
bool isHudVisible;
public void ShowHud(string ProgressText = StaticData.Loading)
{
Device.BeginInvokeOnMainThread(() =>
{
AndHUD.Shared.Show(Forms.Context, ProgressText, maskType: MaskType.Black);
isHudVisible = true;
});
}
public void HideHud()
{
Device.BeginInvokeOnMainThread(() =>
{
AndHUD.Shared.Dismiss(Forms.Context);
isHudVisible = false;
});
}
public void SetProgress(double Progress, string ProgressText = "")
{
if (!isHudVisible)
return;
Device.BeginInvokeOnMainThread(() =>
{
int progress = (int)(Progress * 100);
AndHUD.Shared.Show(Forms.Context, ProgressText + progress + "%", progress, MaskType.Black);
});
}
public void SetText(string Text)
{
if (!isHudVisible)
return;
Device.BeginInvokeOnMainThread(() =>
{
AndHUD.Shared.Show(Forms.Context, Text, maskType: MaskType.Black);
});
}
Android.Views.View CustomLoadingView(string ProgressText)
{
Android.Views.View loadingView = new Android.Views.View(Forms.Context);
return loadingView;
}
#endregion
}
}
iOS
using System;
using BigTed;
using CoreAnimation;
using CoreGraphics;
using XXXX.DependencyServices;
using XXXX.Helpers;
using XXXX.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
[assembly: Dependency(typeof(IosHudService))]
namespace XXXX.iOS
{
/// <summary>
/// Manages loading indicators in the app.
/// </summary>
public class IosHudService : IHudService
{
UIView _load;
bool isHudVisible;
#region IHudManager implementation
/// <summary>
/// Show loading indicator.
/// </summary>
/// <param name="ProgressText">Progress to set.</param>
public void ShowHud(string ProgressText = StaticData.Loading)
{
isHudVisible = true;
SetText(ProgressText);
}
/// <summary>
/// Hide loading indicator.
/// </summary>
public void HideHud()
{
Device.BeginInvokeOnMainThread(() =>
{
BTProgressHUD.Dismiss();
if (_load != null)
_load.Hidden = true;
isHudVisible = false;
});
}
/// <summary>
/// Method to change Progress Text and show custom loader with Challenge Logo
/// </summary>
/// <param name="ProgressText">Progress text.</param>
public void SetProgress(double Progress, string ProgressText = "")
{
int progress = (int)(Progress * 100);
string text = ProgressText + progress + "%";
SetText(text);
}
/// <summary>
/// Set text to loading indicator.
/// </summary>
/// <param name="text">Text to display.</param>
public void SetText(string text)
{
if (!isHudVisible)
return;
Device.BeginInvokeOnMainThread(() =>
{
BTProgressHUD.Show(status: text, maskType: ProgressHUD.MaskType.Black);
try
{
//if (_load == null) {
// _load = CustomLoadingView (text);
// ProgressHUD.Shared.AddSubview (_load);
//}
lblTitle.Text = text;
UIView[] subView = ProgressHUD.Shared.Subviews;
for (int i = 0; i < subView.Length; i++)
{
subView[i].Hidden = true;
}
_load.Hidden = false;
ProgressHUD.Shared.BringSubviewToFront(_load);
}
catch (Exception ex)
{
#if DEBUG
Console.WriteLine("IosHudService.cs - SetText() " + ex.Message);
#endif
}
});
}
UILabel lblTitle;
/// <summary>
/// Customs the loading view.
/// </summary>
/// <returns>The loading view.</returns>
/// <param name="ProgressText">Progress text.</param>
UIView CustomLoadingView(string ProgressText)
{
UIView loadingView = new UIView();
loadingView.Frame = new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height);
UIImageView imgBg = new UIImageView();
imgBg.Image = UIImage.FromFile("load_bg.png");
imgBg.Frame = new CGRect((loadingView.Frame.Width / 2) - 65, (loadingView.Frame.Height / 2) - 70, 130, 140);
loadingView.Add(imgBg);
UIImageView someImageView = new UIImageView();
someImageView.Frame = new CGRect((loadingView.Frame.Width / 2) - 40, (loadingView.Frame.Height / 2) - 50, 75, 75);
someImageView.AnimationImages = new UIImage[]
{
UIImage.FromBundle("spinner.png"),
};
someImageView.AnimationRepeatCount = nint.MaxValue; // Repeat forever.
someImageView.AnimationDuration = 1.0; // Every 1s.
someImageView.StartAnimating();
CABasicAnimation rotationAnimation = new CABasicAnimation();
rotationAnimation.KeyPath = "transform.rotation.z";
rotationAnimation.To = new NSNumber(Math.PI * 2);
rotationAnimation.Duration = 1;
rotationAnimation.Cumulative = true;
rotationAnimation.RepeatCount = float.PositiveInfinity;
someImageView.Layer.AddAnimation(rotationAnimation, "rotationAnimation");
loadingView.Add(someImageView);
lblTitle = new UILabel();
lblTitle.Text = ProgressText;
lblTitle.Frame = new CGRect(imgBg.Frame.X, someImageView.Frame.Y + someImageView.Frame.Height + 15, 130, 20);
lblTitle.TextAlignment = UITextAlignment.Center;
lblTitle.TextColor = UIColor.White;
lblTitle.AdjustsFontSizeToFitWidth = true;
loadingView.Add(lblTitle);
return loadingView;
}
#endregion
}
}
And when ever you need to show it or hide it you can use:
public static void ShowLoadingIndicator(string progressText = "Loading...")
{
Device.BeginInvokeOnMainThread(() =>
{
DependencyService.Get<IHudService>().ShowHud(progressText);
});
}
public static void HideLoadingIndicator()
{
Device.BeginInvokeOnMainThread(() =>
{
DependencyService.Get<IHudService>().HideHud();
});
}
Hope this helps.

Load more items into ListBox when scrolled to bottom

I've got ListBox bound to List. I want to load more items when scrolled to bottom of list so that some animation indicating loading is shown and bound list is expanded. If I understand correctly, I can use ObservableCollection instead of List and expand that collection. Also, I could wrap ItemPresenter into StackPanel with ItemPresenter and Image at bottom.
But how do I detect that list has been scrolled to bottom and initiate expanding of collection?
Check tutorials:
http://blog.slimcode.com/2010/09/11/detect-when-a-listbox-scrolls-to-its-end-wp7/
http://danielvaughan.orpius.com/post/Scroll-Based-Data-Loading-in-Windows-Phone-7.aspx
I found this http://blogs.microsoft.co.il/blogs/shair/archive/2011/04/06/wp7-how-to-extend-listbox-when-reaching-last-item.aspx in my opinion is more coincided, easy and self-explanatory than other examples.
I'm use the following code to solve the same problem. As base I use the following solution, added one DependecyProperties and removed references on assembly
Microsoft.Practices.Prism.Interactivity.
public class ScrollViewMonitor
{
public static readonly DependencyProperty ReachBottomCommandProperty = DependencyProperty.RegisterAttached("ReachBottomCommand",
typeof(ICommand), typeof(ScrollViewMonitor), new PropertyMetadata(null, ReachBottomCommandChanged));
public static ICommand GetReachBottomCommand(DependencyObject dpObj)
{
return (ICommand)dpObj.GetValue(ReachBottomCommandProperty);
}
public static void SetReachBottomCommand(DependencyObject dpObj, ICommand command)
{
dpObj.SetValue(ReachBottomCommandProperty, command);
}
private static FrameworkElement _frmElemenet;
private static void ReachBottomCommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
_frmElemenet = obj as FrameworkElement;
if (_frmElemenet != null)
_frmElemenet.Loaded += frmElement_Loaded;
}
public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.RegisterAttached("VerticalOffset",
typeof(double), typeof(ScrollViewMonitor), new PropertyMetadata(0.0, VerticalOffsetChanged));
private static void VerticalOffsetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
ScrollViewer scrollView = (ScrollViewer)obj;
if (scrollView.VerticalOffset > scrollView.ScrollableHeight * 0.8)
{
ICommand command = GetReachBottomCommand(_frmElemenet);
if (command != null)
command.Execute(null);
}
}
private static void frmElement_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement frmElement = (FrameworkElement)sender;
frmElement.Loaded -= frmElement_Loaded;
var scrollView = FindChildOfType<ScrollViewer>(frmElement);
if (scrollView != null)
scrollView.SetBinding(VerticalOffsetProperty, new Binding("VerticalOffset")
{
Source = scrollView
});
}
private static T FindChildOfType<T>(DependencyObject obj) where T : class
{
Queue<DependencyObject> queue = new Queue<DependencyObject>();
queue.Enqueue(obj); //Adds an object to the end of the
while (queue.Count > 0)
{
DependencyObject current = queue.Dequeue(); //Removes and returns the object at the beginning of the Queue
for (int i = 0, count = VisualTreeHelper.GetChildrenCount(current); i < count; i++)
{
DependencyObject dpObj = VisualTreeHelper.GetChild(current, 0);
T typeChild = dpObj as T;
if (typeChild != null)
return typeChild;
queue.Enqueue(dpObj); //Adds an object to the end of the Queue
}
}
return null;
}
}
In XAML
<ListBox x:Name="_lstBoxNews"
DataContext="{Binding Mode=OneTime}"
ItemsSource="{Binding Items, Mode=OneWay}"
ItemTemplate="{Binding Mode=OneWay, Source={StaticResource ShortArticleTemlate}}"
hlp:ScrollViewMonitor.ReachBottomCommand="{Binding LoadAdditionalArticles, Mode=OneTime}"
>
</ListBox>

Resources