Xamarin.Forms: Remove indentation when using a disclosure button - c#

I want to use a disclosure button on my table view cell. Therefore I use this custom renderer:
using HelloXamarinFormsWorld;
using HelloXamarinFormsWorld.iOS;
using UIKit;
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using System.Drawing;
using ObjCRuntime;
/* Example of using a custom renderer to get the > disclosure indicator to appear */
[assembly: ExportRenderer (typeof (EmployeeCell), typeof (EmployeeCellRenderer))]
namespace HelloXamarinFormsWorld.iOS
{
public class EmployeeCellRenderer : ViewCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var cell = base.GetCell (item, reusableCell, tv);
cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
// // Remove seperator inset
// if (cell.RespondsToSelector (new Selector ("setSeparatorInset:"))) {
// cell.SeparatorInset = UIEdgeInsets.Zero;
// }
// // Prevent the cell from inheriting the Table View's margin settings
// if (cell.RespondsToSelector (new Selector ("setPreservesSuperviewLayoutMargins:"))) {
// cell.PreservesSuperviewLayoutMargins = false;
// }
// // Explictly set your cell's layout margins
// if (cell.RespondsToSelector (new Selector ("setLayoutMargins:"))) {
// cell.LayoutMargins = UIEdgeInsets.Zero;
// }
return cell;
}
}
}
If I set the disclosure indicator I get the following screen:
Normally it does look like the following
I tried to play with the inset, but that has to be in WillDisplay and also the indentation is not removed. In this thread rmarinho states
you can fix by setting the UIEdgeInsets i think , i use the Padding property on ExtendedViewCell for this.
but I don't know where I should do this. Or how do I fix the indentation?
Edit:
Using cell.SeparatorInset = new UIEdgeInsets(0,0,0,0); didn't help.

I guess it should be a bug in Xamarin.Forms.
After looking into the decompiled code, I have found that the default ViewCellRenderer tries to center itself by calculating the content view of the cell, which is not correct as it didn't consider the size of DisclosureIndicator.
So, the workaround is to insert an indicator manually (that's what ExtendedViewCell did) and do not use the default disclosure indicator.

Related

How can I add word wrap to EditorGUILayout.TextArea?

How can I add word wrap to the editor textarea? I'm trying to mimic the [TextArea] attribute (word wrap, automatically increase height when needed)
I know that GUILayout.TextArea() works but I was hoping to use EditorGUILayout because according to the docs it correctly responds to copy/pasting, select all, etc.
My code:
obj.talkableFlavorText = EditorGUILayout.TextArea(obj.talkableFlavorText, GUILayout.MinHeight(textAreaHeight));
Use a GUIStyle and set the wordWrap property to true.
Complete example based on Unity Editor Window Example
using UnityEngine;
using UnityEditor;
public class MyWindow : EditorWindow
{
string myString = "Hello World";
// Add menu named "My Window" to the Window menu
[MenuItem("Window/My Window")]
static void Init()
{
// Get existing open window or if none, make a new one:
MyWindow window = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow));
window.Show();
}
void OnGUI()
{
GUIStyle style = new GUIStyle(EditorStyles.textArea);
style.wordWrap = true;
myString = EditorGUILayout.TextArea(myString, style);
}
}
Result:

Switching between "Tabs" (via bottom navigation) - flickering, any advice?

I am currently writing a Messenger app for my Guild, currently I got as far as, I can log in and switch between different tabs, that lists currently dummy message titles (like a whisper name).
My goal later is that once you click on one of the messages you can reply / read messages in WhatsApp style (tips welcome here as well).
But my current issue is that I use the "Bottom" navigation menu. I swap the tabs currently with new activies. But whenever I click a button the screen "flickers" like, it's restarting the whole app.
Is there some way to switch "fluid" the upper part of the app, while the menu always stays nicely at the bottom?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Views;
using Android.Widget;
namespace GuildMaster
{
[Activity(Label = "Whisper")]
public class Whisper : Activity, BottomNavigationView.IOnNavigationItemSelectedListener
{
private ListView whisperlist;
private List<string> itemlist;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
BottomNavigationView navigation = FindViewById<BottomNavigationView>(Resource.Id.navigation);
navigation.SetOnNavigationItemSelectedListener(this);
whisperlist = FindViewById<ListView>(Resource.Id.whisper);
itemlist = new List<string>();
itemlist.Add("Tim");
itemlist.Add("Tom");
ArrayAdapter<string> whisper = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, itemlist);
whisperlist.Adapter = whisper;
whisperlist.ItemClick += Listnames_ItemClick;
// Create your application here
}
public void Listnames_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
Toast.MakeText(this, e.Position.ToString(), ToastLength.Long).Show();
}
public bool OnNavigationItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Resource.Id.navigation_home:
StartActivity(typeof(Whisper));
return true;
case Resource.Id.navigation_dashboard:
StartActivity(typeof(Guild));
return true;
case Resource.Id.navigation_notifications:
StartActivity(typeof(Other));
return true;
}
return false;
}
}
}
you should try working with fragments instead of activities with the bottom navigation, working with an activity means u have to start a completely new screen with a new bottom navigation, if that is what you are doing then it is completely normal to "flicker" when doing so
as a solution, use an activity with an xml that holds a fragment view and a bottom navigation view, and when u switch from the bottom navigation in your activity, switch the fragments loaded within the fragment view.
this can be done with different methods, but i would suggest checking out the Navigation Component from Google, since it will also handle the backstack for you.

Xamarin Forms Nested Tabs

I've noticed it's possible to create a mobile app with nested tabs.
Is this possible in Xamarin Forms?
Please see screen shot below:
I can create the bottom tabs on iOS using TabbedPage, but how do I create the nested tabs at the top of the page?
Thank you
The same way you would do on the native app. There are no native nested tabs, so Xamarin can't support it as such thing doesn't exist.
In the native app you have the control at the top (called SegmentedControl on iOS and on Android there is no such control out of the box) where you pick the value and then change the view below manually when it is clicked.
Is this possible in Xamarin Forms?
Yes,of course.You can use CustomRenderer to implement it.Refer to the following code.
in iOS Project . Create a pageRenderer
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using app1;
using app1.iOS;
using UIKit;
using Foundation;
using CoreGraphics;
using ObjCRuntime;
[assembly:ExportRenderer(typeof(MyPage1),typeof(MyPageRenderer))]
namespace app1.iOS
{
public class MyPageRenderer:PageRenderer
{
public MyPageRenderer()
{
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (ViewController != null)
{
NSArray items = NSArray.FromStrings(new string[] { "Courses", "Favourite", "Recent" });
UISegmentedControl segmentedControl = new UISegmentedControl(items)
{
Frame = new CGRect(50, 20, NativeView.Bounds.Width - 100, 35)
};
segmentedControl.SelectedSegment = 0;
segmentedControl.TintColor = UIColor.Red;
segmentedControl.ApportionsSegmentWidthsByContent = true; //Change the width of the segment based on the content of the segment
segmentedControl.AddTarget(this, new Selector("ValueChanged:"), UIControlEvent.ValueChanged);
NativeView.AddSubview(segmentedControl);
}
}
[Export("ValueChanged:")]
void ValueChanged(UISegmentedControl sender)
{
MessagingCenter.Send<Object, int>(this, "ClickSegmentedControl", (int)sender.SelectedSegment);
// switch((int)sender.SelectedSegment){
// case 0:
// break;
// case 1:
// break;
// case 2:
// break;
// default:
// break;
//}
}
}
}
in Forms ,you can subscribe the message .if you want to handle the event in forms when you click the segmented .
public MyPage1()
{
//...
MessagingCenter.Subscribe<Object, int>(this, "ClickSegmentedControl", (sender, arg) =>
{
Console.WriteLine(arg); //arg is num of the segment that you clicked.
});
}

UWP - GridView without Xaml templates

Long story short.
I have an UWP UI which contains a GridView and does not use Xaml. I'd like to display fully code-behind constructed items. No Xaml templates.
I have figured out that the GridView's ChoosingItemContainer event will allow me to create the GridViewItem instances programmatically and even possibly reuse them.
However the custom UI of the items is not actually displayed.
I have noticed that when scrolling a large amount of data the content appears very briefly and then it disappears. I'm guessing that the GridViewItem's Content is being overwritten by some kind of default template. Is there a way to disable this machinery?
More generally speaking, is there a known way to use GridView + Items without Xaml at all?
UPDATE:
Here is a minimal code sample that demonstrates the problem.
Place the CustomGridView somewhere in your UI.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
namespace MyApp
{
// Some kind of data object
public class MyData
{
public string MyProperty;
}
// A custom GridViewItem
public class MyGridViewItem : GridViewItem
{
private TextBox mTextBox;
public MyGridViewItem()
{
mTextBox = new TextBox();
mTextBox.Width = 100;
mTextBox.Height = 100;
Content = mTextBox;
// Make the items visible at all: use red background
Background = new SolidColorBrush(Color.FromArgb(255,255,0,0));
}
public void SetData(MyData d)
{
mTextBox.Text = d.MyProperty;
// Content seems to be always reset to the data object itself.
Content = mTextBox;
// With the following line the contents appear briefly while the view is scrolling.
// Without this line the contents don't appear at all
Template = null;
}
}
// Custom grid. No Xaml.
public class CustomGridView : GridView
{
public CustomGridView()
{
this.ChoosingItemContainer += CustomGridView_ChoosingItemContainer;
// Create some data to show.
CollectionViewSource s = new CollectionViewSource();
ObservableCollection<MyData> oc = new ObservableCollection<MyData>();
for(int i = 0;i < 10000;i++)
{
MyData d = new MyData();
d.MyProperty = i.ToString();
oc.Add(d);
}
s.Source = oc;
ItemsSource = s.View;
}
private void CustomGridView_ChoosingItemContainer(ListViewBase sender,ChoosingItemContainerEventArgs args)
{
// Unchecked cast, but for the sake of simplicity let's assume it always works.
MyData d = (MyData)args.Item;
MyGridViewItem it = null;
if((args.ItemContainer != null) && (args.ItemContainer.GetType() == typeof(MyGridViewItem)))
it = (MyGridViewItem)args.ItemContainer;
else
it = new MyGridViewItem();
it.SetData(d);
args.ItemContainer = it;
args.IsContainerPrepared = true;
// This should probably go elsewhere, but for simplicity it's here :)
((ItemsWrapGrid)ItemsPanelRoot).ItemWidth = 100;
((ItemsWrapGrid)ItemsPanelRoot).ItemHeight = 100;
}
}
}
For you can custom UI by new a style to set.
You can set the resource in xaml,and give it a key ,like "res".
And you can get GridView.Style=(Style)Resources["res"]; in code.
To use style in xaml is a good way.But you can set style in code see:
Style style = new Style(typeof (GridView));
style.Setters.Add(new Setter(GridView.Property, xx));
GridView.Style=style ;
The style can be set the GridView and Items just like the xaml.
You have no say that what ui you want that I cant give you a code.

Setting controls in a Telerik GridView cell

Here's a concrete example of what I am attempting to do with the Telerik GridView control. Let's say I have an application that will read a delimited file (say CSV) which will have n columns. The value n can vary from file-to-file but is constant within a file. The application starts with an empty grid and adds columns as required to match the input file. The grid displays all data as strings in all cells. This works with either binding to a BindingList or putting the record (objects) into the GridView.Items list.
Now, what I want to do is put a single row at the top of the grid (a row that will not scroll) that contains comboboxes. That is, at the top of each column, the first row contains a combobox. On the first pass, the combobox will only be a drop list, but on the next pass I will add another row with a set of comboboxes that will be editable. For now, let's only consider drop lists.
The specific problem that I have is that I do not see how to set a specific type of control for a particular cell. Telerik provides a GridViewComboBoxColumn class that will define the behavior for an entire column but that's not what I need.
Because of the variable number of columns, I think that the code-behind would be the place to work this magic. I may have to do something in the xaml but, since I've only been in WPF for a few months, nothing is jumping out at me.
I've done something like this with the DataGridView and XtraGrid, but this one has me stumped. Pointers would be much appreciated.
In response to Jonathan D's answer, I have taken the provided code and modified it to recognize when a cell on the 0th row is being edited. When this is the case, a drop list is presented when the user initiates an edit operation.
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.GridView;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace LLamasoft.DataGuru.Plugin.Internal.ConfigurationUI
{
public class RadGridViewComboboxHeaderColumn : GridViewBoundColumnBase
{
public static readonly DependencyProperty SelectedStringProperty =
DependencyProperty.Register("SelectedString", typeof(string), typeof(RadGridViewComboboxHeaderColumn), new PropertyMetadata(null));
public string SelectedString
{
get { return (string) GetValue(SelectedStringProperty); }
set { SetValue(SelectedStringProperty, value); }
}
public override FrameworkElement CreateCellEditElement(GridViewCell cell, object dataItem)
{
// we need the row on which this cell lives
GridViewDataControl gridViewDataControl = cell.ParentRow.GridViewDataControl;
object currentEditItem = gridViewDataControl.Items.CurrentEditItem;
int index = gridViewDataControl.Items.IndexOf(currentEditItem);
FrameworkElement frameworkElement = null;
if (index == 0)
{
BindingTarget = ComboBox.SelectedValueProperty;
ComboBox comboBox = new ComboBox();
// seed some values,
// this list should be set right after construction if static, otherwise via callback if dynamic
comboBox.Items.Add(string.Empty);
comboBox.Items.Add("apples");
comboBox.Items.Add("oranges");
if (!comboBox.Items.Contains(cell.Value))
{
comboBox.Items.Add(cell.Value);
}
comboBox.SelectedValue = SelectedString;
frameworkElement = comboBox;
}
else
{
BindingTarget = TextBox.TextProperty;
TextBox textBox = new TextBox();
textBox.Text = SelectedString;
frameworkElement = textBox;
}
frameworkElement.SetBinding(BindingTarget, CreateValueBinding());
return frameworkElement;
}
public override object GetNewValueFromEditor(object editor)
{
// ensure that the control will return the correct value when queried for it
ComboBox comboBox = editor as ComboBox;
if (comboBox != null)
{
// bound to comboBox.SelectedValue which carries the correct value
}
TextBox textBox = editor as TextBox;
if (textBox != null)
{
// bound to textBox.Text which carries the correct value
}
return base.GetNewValueFromEditor(editor);
}
private Binding CreateValueBinding()
{
Binding valueBinding = new Binding();
valueBinding.Mode = BindingMode.TwoWay;
valueBinding.NotifyOnValidationError = true;
valueBinding.ValidatesOnExceptions = true;
valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
valueBinding.Path = new PropertyPath(this.DataMemberBinding.Path.Path);
return valueBinding;
}
}
}
The good news is that this shows that any edit control can be used in any cell based on requirements.
The bad parts are: 1) a dummy record has to be inserted at the 0th list position and must be maintained there, 2) the data is being stored back into the field on the 0th record and may require a different data type than is on the equivalent fields on the other records, and 3) the combobox is only shown when the cell is in the edit mode.
The latter issue for me may not be an issue elsewhere. I want a visual cue that the user is expected to interact with the cells at the top of the columns. Using this method, there is no differentiating factor between the top row and the rest of the rows until the edit operation begins. My ideal solution would have the cells always show their comboboxes.
One other issue that I find difficult to believe that I am facing is the fact that I cannot easily pin/freeze topmost rows. I want this line to always remain at the top after scrolling. There is no _grid.Rows[0].IsPinned = true functionality.
Telerik has responded to my request for info and suggests that I use a template selector to determine how the cell is represented. (http://www.telerik.com/community/forums/wpf/gridview/need-just-first-row-in-grid-to-be-all-comboboxes.aspx#1820310). At this point, I turn my attention to testing that method.
You want to create your own column
using System.Windows;
using System.Windows.Data;
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.GridView;
using System;
namespace Inspect
{
public class DateTimePickerColumn : GridViewBoundColumnBase
{
public TimeSpan TimeInterval
{
get
{
return (TimeSpan) GetValue(TimeIntervalProperty);
}
set
{
SetValue(TimeIntervalProperty, value);
}
}
public static readonly DependencyProperty TimeIntervalProperty =
DependencyProperty.Register("TimeInterval", typeof(TimeSpan), typeof(DateTimePickerColumn), new PropertyMetadata(TimeSpan.FromHours(1d)));
public override FrameworkElement CreateCellEditElement(GridViewCell cell, object dataItem)
{
this.BindingTarget = RadDateTimePicker.SelectedValueProperty;
RadDateTimePicker picker = new RadDateTimePicker();
picker.IsTooltipEnabled = false;
picker.TimeInterval = this.TimeInterval;
picker.SetBinding(this.BindingTarget, this.CreateValueBinding());
return picker;
}
public override object GetNewValueFromEditor(object editor)
{
RadDateTimePicker picker = editor as RadDateTimePicker;
if (picker != null)
{
picker.DateTimeText = picker.CurrentDateTimeText;
}
return base.GetNewValueFromEditor(editor);
}
private Binding CreateValueBinding()
{
Binding valueBinding = new Binding();
valueBinding.Mode = BindingMode.TwoWay;
valueBinding.NotifyOnValidationError = true;
valueBinding.ValidatesOnExceptions = true;
valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
valueBinding.Path = new PropertyPath(this.DataMemberBinding.Path.Path);
return valueBinding;
}
}
}
That is how you create a custom column. If you modify the CreateCellEditElement Method it will let you create custom cells how you like. You should even be able to detect the row number

Categories