[英]In the C# back end, how can I select an element in a Segmented Control?
I'm using a custom control to create joined buttons that display like this: 我正在使用自定义控件来创建如下所示的连接按钮:
********************************************
* * * *
* One * Two * Three *
* * * *
********************************************
When I click the buttons then an event is fired: 单击按钮时会触发一个事件:
void OnValueChanged(object sender, EventArgs e)
{
switch (segControl.SelectedValue)
{
This is working well but what I would like to do is to be able to select one of those buttons from within my C# code. 这很好用,但我想做的是能够从我的C#代码中选择其中一个按钮。
When I look at the source code for the control I cannot see how to do this. 当我查看控件的源代码时,我看不到如何执行此操作。
Would appreciate if anyone has any suggestions on what I should do to for example select the 2nd button in the same way as if I had clicked on it. 如果有人对我应该做什么有任何建议,例如选择第二个按钮,就像我点击它一样,我将不胜感激。
Here is the code I am using: 这是我正在使用的代码:
<local:SegmentedControl ValueChanged="OnValueChanged" SelectedSegment="{Binding CustomPointsSwitch}" x:Name="segControl" HorizontalOptions="End">
<local:SegmentedControl.Children>
<local:SegmentedControlOption Text="Two" />
<local:SegmentedControlOption Text="Four" />
</local:SegmentedControl.Children>
</local:SegmentedControl>
Shared Code 共享代码
public class SegmentedControl : View, IViewContainer<SegmentedControlOption>
{
public IList<SegmentedControlOption> Children { get; set; }
public SegmentedControl()
{
Children = new List<SegmentedControlOption>();
}
public event ValueChangedEventHandler ValueChanged;
public delegate void ValueChangedEventHandler(object sender, EventArgs e);
private string selectedValue;
public string SelectedValue
{
get { return selectedValue; }
set
{
selectedValue = value;
if (ValueChanged != null)
ValueChanged(this, EventArgs.Empty);
}
}
public static readonly BindableProperty SelectedSegmentProperty = BindableProperty.Create("SelectedSegment", typeof(int), typeof(SegmentedControl), 0);
public int SelectedSegment
{
get
{
return (int)GetValue(SelectedSegmentProperty);
}
set
{
SetValue(SelectedSegmentProperty, value);
}
}
}
public class SegmentedControlOption : View
{
public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(SegmentedControlOption), string.Empty);
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
iOS Renderer iOS渲染器
public class SegmentedControlRenderer : ViewRenderer<SegmentedControl, UISegmentedControl>
{
public SegmentedControlRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<SegmentedControl> e)
{
base.OnElementChanged(e);
var segmentedControl = new UISegmentedControl();
for (var i = 0; i < e.NewElement.Children.Count; i++)
{
segmentedControl.InsertSegment(e.NewElement.Children[i].Text, i, false);
}
segmentedControl.ValueChanged += (sender, eventArgs) => {
e.NewElement.SelectedSegment = (int)segmentedControl.SelectedSegment;
e.NewElement.SelectedValue = segmentedControl.TitleAt(segmentedControl.SelectedSegment);
};
SetNativeControl(segmentedControl);
}
}
Android Renderer Android渲染器
public class SegmentedControlRenderer : ViewRenderer<SegmentedControl, RadioGroup>
{
protected override void OnElementChanged(ElementChangedEventArgs<SegmentedControl> e)
{
base.OnElementChanged(e);
var layoutInflater = (LayoutInflater)Context.GetSystemService(Context.LayoutInflaterService);
var g = new RadioGroup(Context);
g.Orientation = Orientation.Horizontal;
g.CheckedChange += (sender, eventArgs) => {
var rg = (RadioGroup)sender;
if (rg.CheckedRadioButtonId != -1)
{
var id = rg.CheckedRadioButtonId;
var radioButton = rg.FindViewById(id);
var radioId = rg.IndexOfChild(radioButton);
var btn = (RadioButton)rg.GetChildAt(radioId);
var selection = (String)btn.Text;
e.NewElement.SelectedValue = selection;
}
};
for (var i = 0; i < e.NewElement.Children.Count; i++)
{
var o = e.NewElement.Children[i];
var v = (SegmentedControlButton)layoutInflater.Inflate(Resource.Layout.SegmentedControl, null);
v.Text = o.Text;
if (i == 0)
v.SetBackgroundResource(Resource.Drawable.segmented_control_first_background);
else if (i == e.NewElement.Children.Count - 1)
v.SetBackgroundResource(Resource.Drawable.segmented_control_last_background);
g.AddView(v);
}
SetNativeControl(g);
}
}
public class SegmentedControlButton : RadioButton
{
private int lineHeightSelected;
private int lineHeightUnselected;
private Paint linePaint;
public SegmentedControlButton(Context context, IAttributeSet attributes) : this(context, attributes, Resource.Attribute.segmentedControlOptionStyle)
{
}
public SegmentedControlButton(Context context, IAttributeSet attributes, int defStyle) : base(context, attributes, defStyle)
{
Initialize(attributes, defStyle);
}
private void Initialize(IAttributeSet attributes, int defStyle)
{
var a = this.Context.ObtainStyledAttributes(attributes, Resource.Styleable.SegmentedControlOption, defStyle, Resource.Style.SegmentedControlOption);
var lineColor = a.GetColor(Resource.Styleable.SegmentedControlOption_lineColor, 0);
linePaint = new Paint();
linePaint.Color = lineColor;
lineHeightUnselected = a.GetDimensionPixelSize(Resource.Styleable.SegmentedControlOption_lineHeightUnselected, 0);
lineHeightSelected = a.GetDimensionPixelSize(Resource.Styleable.SegmentedControlOption_lineHeightSelected, 0);
a.Recycle();
}
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
if (linePaint.Color != 0 && (lineHeightSelected > 0 || lineHeightUnselected > 0))
{
var lineHeight = Checked ? lineHeightSelected : lineHeightUnselected;
if (lineHeight > 0)
{
var rect = new Rect(0, Height - lineHeight, Width, Height);
canvas.DrawRect(rect, linePaint);
}
}
}
}
I would appreciate any suggestions on how I can make the change. 我很感激有关如何进行更改的任何建议。 Also does anyone have an insights as to if Xamarin are looking to make this into a forms component?
也有人知道Xamarin是否希望将其变成表单组件?
Why not just set margins in shared xaml? 为什么不在共享xaml中设置边距?
<local:SegmentedControl ValueChanged="OnValueChanged" SelectedSegment="{Binding CustomPointsSwitch}" x:Name="segControl" HorizontalOptions="End">
<local:SegmentedControl.Children>
<local:SegmentedControlOption Text="Two" Margin="8,0,8,0" />
<local:SegmentedControlOption Text="Four" Margin="8,0,8,0" />
</local:SegmentedControl.Children>
</local:SegmentedControl>
Have you had a chance to look at this plugin - it seems perfectly suited for what you are trying to achieve. 你有没有机会看看这个插件 - 它似乎非常适合你想要实现的目标。
For being able to programmatically select a segment - ie SelectedSegment
- you need to implement this at renderer level, and have them translate this selection to native controls. 为了能够以编程方式选择一个段 - 即
SelectedSegment
- 您需要在渲染器级别实现它,并让它们将此选择转换为本机控件。 Furthermore, it is also recommended to override OnElementPropertyChanged
method to subscribe to property changes (such as SelectedSegment
) and ensure that the native control state stays in sync with the shared forms element, and vice-versa. 此外,还建议重写
OnElementPropertyChanged
方法以订阅属性更改(例如SelectedSegment
)并确保本机控件状态与共享表单元素保持同步,反之亦然。
EDIT - 1: Add code sample 编辑 - 1:添加代码示例
Shared code 共享代码
public class SegmentedControl : View, IViewContainer<SegmentedControlOption>
{
public IList<SegmentedControlOption> Children { get; set; }
public SegmentedControl()
{
Children = new List<SegmentedControlOption>();
}
public event EventHandler ValueChanged;
public static readonly BindableProperty SelectedValueProperty =
BindableProperty.Create(
"SelectedValue", typeof(string), typeof(SegmentedControl),
defaultBindingMode: BindingMode.TwoWay,
defaultValue: default(string), propertyChanged: OnSelectedValueChanged);
public string SelectedValue
{
get { return (string)GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
static void OnSelectedValueChanged(BindableObject bindable, object oldValue, object newValue)
{
((SegmentedControl)bindable).OnSelectedValueChangedImpl((string)oldValue, (string)newValue);
}
protected virtual void OnSelectedValueChangedImpl(string oldValue, string newValue)
{
ValueChanged?.Invoke(this, EventArgs.Empty);
SelectedSegment = GetSelectedIndex(SelectedValue);
}
public static readonly BindableProperty SelectedSegmentProperty =
BindableProperty.Create(
"SelectedSegment", typeof(int), typeof(SegmentedControl),
defaultBindingMode: BindingMode.TwoWay,
defaultValue: default(int), propertyChanged: OnSelectedSegmentChanged);
public int SelectedSegment
{
get { return (int)GetValue(SelectedSegmentProperty); }
set { SetValue(SelectedSegmentProperty, value); }
}
private static void OnSelectedSegmentChanged(BindableObject bindable, object oldValue, object newValue)
{
((SegmentedControl)bindable).OnSelectedSegmentChangedImpl((int)oldValue, (int)newValue);
}
protected virtual void OnSelectedSegmentChangedImpl(int oldValue, int newValue)
{
SelectedValue = GetSelectedValue(SelectedSegment);
}
int GetSelectedIndex(object selectedItem)
{
if (selectedItem == null)
return -1;
if (selectedItem is string optionText)
return Children.IndexOf(Children.FirstOrDefault(x => Equals(x.Text, optionText)));
return -1;
}
string GetSelectedValue(int index)
{
if (index >= 0 && index < Children.Count)
return Children[index].Text;
return null;
}
}
//Keep SegmentedControlOption as same (No changes needed)
public class SegmentedControlOption : View
{
...
}
iOS renderer iOS渲染器
public class SegmentedControlRenderer : ViewRenderer<SegmentedControl, UISegmentedControl>
{
protected override void OnElementChanged(ElementChangedEventArgs<SegmentedControl> e)
{
base.OnElementChanged(e);
UISegmentedControl segmentedControl = null;
if (Control == null)
{
segmentedControl = new UISegmentedControl();
for (var i = 0; i < e.NewElement.Children.Count; i++)
{
segmentedControl.InsertSegment(Element.Children[i].Text, i, false);
}
SetNativeControl(segmentedControl);
SetSelectedSegment();
}
if (e.OldElement != null)
{
// Unsubscribe from event handlers and cleanup any resources
if (segmentedControl != null)
segmentedControl.ValueChanged -= NativeValueChanged;
}
if (e.NewElement != null)
{
// Configure the control and subscribe to event handlers
segmentedControl.ValueChanged += NativeValueChanged;
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(SegmentedControl.SelectedSegment))
SetSelectedSegment();
}
void NativeValueChanged(object sender, EventArgs e)
{
if (Element is SegmentedControl formsElement)
{
formsElement.SelectedSegment = (int)Control.SelectedSegment;
};
}
void SetSelectedSegment()
{
if (Element is SegmentedControl formsElement)
{
if (formsElement.SelectedSegment >= 0 && formsElement.SelectedSegment < Control.NumberOfSegments)
Control.SelectedSegment = formsElement.SelectedSegment;
}
}
}
Android renderer Android渲染器
public class SegmentedControlRenderer : ViewRenderer<SegmentedControl, RadioGroup>
{
protected override void OnElementChanged(ElementChangedEventArgs<SegmentedControl> e)
{
base.OnElementChanged(e);
RadioGroup nativeControl = null;
if (Control == null)
{
// Instantiate the native control and assign it to the Control property with the SetNativeControl method
var layoutInflater = (LayoutInflater)Context.GetSystemService(Context.LayoutInflaterService);
nativeControl = new RadioGroup(Context)
{
Orientation = Orientation.Horizontal
};
for (var i = 0; i < e.NewElement.Children.Count; i++)
{
var o = e.NewElement.Children[i];
var v = (SegmentedControlButton)layoutInflater.Inflate(Resource.Layout.SegmentedControl, null);
v.Text = o.Text;
if (i == 0)
v.SetBackgroundResource(Resource.Drawable.segmented_control_first_background);
else if (i == e.NewElement.Children.Count - 1)
v.SetBackgroundResource(Resource.Drawable.segmented_control_last_background);
nativeControl.AddView(v);
}
SetNativeControl(nativeControl);
SetSelectedSegment();
}
if (e.OldElement != null)
{
// Unsubscribe from event handlers and cleanup any resources
if (nativeControl != null)
nativeControl.CheckedChange -= NativeCheckedChanged;
}
if (e.NewElement != null)
{
nativeControl.CheckedChange += NativeCheckedChanged;
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(SegmentedControl.SelectedSegment))
SetSelectedSegment();
}
void NativeCheckedChanged(object sender, EventArgs e)
{
if (Element is SegmentedControl formsElement)
{
var rg = (RadioGroup)sender;
if (rg.CheckedRadioButtonId != -1)
{
var id = rg.CheckedRadioButtonId;
var radioButton = rg.FindViewById(id);
var radioIndex = rg.IndexOfChild(radioButton);
formsElement.SelectedSegment = radioIndex;
}
};
}
void SetSelectedSegment()
{
if (Element is SegmentedControl formsElement)
{
if (formsElement.SelectedSegment >= 0 && formsElement.SelectedSegment < Control.ChildCount)
{
var radioBtn = (RadioButton)Control.GetChildAt(formsElement.SelectedSegment);
radioBtn.Checked = true;
}
}
}
}
//Keep SegmentedControlOption as same (No changes needed)
public class SegmentedControlButton : RadioButton
{
...
}
<StackLayout Margin="20">
<local:SegmentedControl ValueChanged="OnValueChanged" SelectedSegment="{Binding CustomPointsSwitch}" x:Name="segControl" HorizontalOptions="End">
<local:SegmentedControl.Children>
<local:SegmentedControlOption Text="One" />
<local:SegmentedControlOption Text="Two" />
<local:SegmentedControlOption Text="Three" />
</local:SegmentedControl.Children>
</local:SegmentedControl>
<Label Text="{Binding Path=SelectedSegment, StringFormat='Selected Segment: {0}', Source={x:Reference segControl}}" />
<Label Text="{Binding Path=SelectedValue, StringFormat='Selected Value: {0}', Source={x:Reference segControl}}" />
<StackLayout Orientation="Horizontal">
<Button Text="Select One" Command="{Binding SelectCommand}" CommandParameter="0" HorizontalOptions="Start" />
<Button Text="Select Two" Command="{Binding SelectCommand}" CommandParameter="1" HorizontalOptions="CenterAndExpand" />
<Button Text="Select Three" Command="{Binding SelectCommand}" CommandParameter="2" HorizontalOptions="End" />
</StackLayout>
<Button Text="Set selection using SelectedValue" Clicked="Handle_Clicked" />
</StackLayout>
Code behind 代码背后
public partial class SegmentedSamplePage : ContentPage
{
public SegmentedSamplePage()
{
InitializeComponent();
this.BindingContext = new PageViewModel
{
CustomPointsSwitch = 1
};
}
void OnValueChanged(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine(this.segControl.SelectedValue);
}
void Handle_Clicked(object sender, System.EventArgs e)
{
this.segControl.SelectedValue = "Two";
}
}
public class PageViewModel : ObservableObject
{
int _customPointsSwitch;
public int CustomPointsSwitch
{
get { return _customPointsSwitch; }
set
{
SetProperty(ref _customPointsSwitch, value, nameof(CustomPointsSwitch));
}
}
Command _selectCommand;
public Command SelectCommand => _selectCommand ?? (_selectCommand = new Command((object param) =>
{
if (Int32.TryParse(param?.ToString(), out int index))
CustomPointsSwitch = index;
}));
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.