I have created a custom iOS control in a Xamarin project. However, it currently does not draw on to the page when added into the XAML on a Xamarin.Forms page.
<OnPlatform.iOS>
<iOSControls:CellContent BindingContext="{Binding}" CellData="{Binding VM.CellCollection}"/>
</OnPlatform.iOS>
I am also not sure if this is the correct way to bind data from the ViewModel on to the control, and due to the fact it does not load am unable to test this.
The CellContent class is as below
`Public partial class CellContent : UIView
{
#region [ Private Fields ]
/// <summary>
/// The cell data model used for generating each cell
/// </summary>
private ICell cellData;
#endregion
#region [ Constructor ]
/// <summary>
/// Initializes a new instance of the <see cref="CellContent" /> class.
/// </summary>
/// <param name="handle">handle pointer passed to the base class</param>
public CellContent(IntPtr handle) : base(handle)
{
}
#endregion
#region [ Public Properties ]
/// <summary>
/// Gets or sets the cell data model
/// </summary>
[Export("CellData"), Browsable(true)]
public ICell CellData
{
get
{
return this.cellData;
}
set
{
this.cellData = value;
}
}
#endregion
#region [ UIView Events ]
/// <summary>
/// Static control generator
/// </summary>
/// <returns>A control instance</returns>
public static CellContent Create()
{
var arr = NSBundle.MainBundle.LoadNib("CellContent", null, null);
var v = Runtime.GetNSObject<CellContent>(arr.ValueAt(0));
return v;
}
/// <summary>
/// Initialises the sub controls
/// </summary>
public override void AwakeFromNib()
{
base.AwakeFromNib();
HorizontalGridLines horizontalRows = HorizontalGridLines.Create();
VerticalGridLines verticalRows = VerticalGridLines.Create();
PlottedActivity plottedActivity = PlottedActivity.Create();
horizontalRows.VM = this.CellData;
verticalRows.VM = this.CellData;
plottedActivity.VM = this.CellData;
this.AddSubview(horizontalRows);
this.AddSubview(verticalRows);
this.AddSubview(plottedActivity);
}
#endregion`
The horizontal/vertical grid lines and plotted activity files are all basically the same and I have verified they work in a separate project (without the binding or being instantiated from the XAML), but the HorizontalGridLines file can be seen below as well for reference
`public partial class HorizontalGridLines : UIView
{
/// <summary>
/// Initializes a new instance of the <see cref="HorizontalGridLines" /> class.
/// </summary>
/// <param name="handle">handle pointer passed to the base class</param>
public HorizontalGridLines(IntPtr handle) : base(handle)
{
}
/// <summary>
/// Gets or sets the data context cast as an interface for binding
/// </summary>
[Export("CellData"), Browsable(true)]
public ICell CellData{ get; set; }
/// <summary>
/// Static control generator
/// </summary>
/// <returns>A control instance</returns>
public static HorizontalGridLines Create()
{
var arr = NSBundle.MainBundle.LoadNib("HorizontalGridLines", null, null);
var v = Runtime.GetNSObject<HorizontalGridLines>(arr.ValueAt(0));
return v;
}
/// <summary>
/// Drawing override
/// </summary>
/// <param name="rect">The content bounds</param>
public override void Draw(CGRect rect)
{
base.Draw(rect);
int numOfLines = this.CellData.ActivityRows.Count;
var context = UIGraphics.GetCurrentContext();
context.SetLineWidth(2);
context.SetLineDash(0, new nfloat[] { 3, 4 });
UIColor.Black.SetStroke();
int y = 0;
int width = (int)this.Frame.Width;
int height = (int)this.Frame.Height;
int heightPerLine = y = height / numOfLines;
for (int i = 0; i < numOfLines - 1; i++)
{
var path = new CGPath();
CGPoint[] lines = new CGPoint[2];
lines[0] = new PointF(0, y);
lines[1] = new PointF(width, y);
y += heightPerLine;
path.AddLines(lines);
path.CloseSubpath();
context.AddPath(path);
}
context.DrawPath(CGPathDrawingMode.FillStroke);
}
}`
Any help would be greatly appreciated, thanks! :)
So I discovered the way to do this:
On the custom control that you have created, create a public property that you will be assigning your data to.
On the XAML assign the {Binding .} to the public property you have just created providing that they are of the same type.
The data will be accessible from within the custom control during/after the LayoutSubviews() method.
For Example:
<iOSControls:CellContent CellData="{Binding .}"/>
The BindingContext property seems to be a part of standard Xamarin controls rather than custom native controls.
Related
Below is the standard d3drenderer.cs file coding:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using System.Xml.Linq;
namespace WPFMediaKit.DirectShow.Controls
{
/// <summary>
/// The D3DRenderer class provides basic functionality needed
/// to render a D3D surface. This class is abstract.
/// </summary>
public abstract class D3DRenderer : FrameworkElement
{
private string workingDirectory = AppDomain.CurrentDomain.BaseDirectory;
/// <summary>
/// The D3DImage used to render video
/// </summary>
private D3DImage m_d3dImage;
/// <summary>
/// The Image control that has the source
/// to the D3DImage
/// </summary>
private Image m_videoImage;
/// <summary>
/// We keep reference to the D3D surface so
/// we can delay loading it to avoid a black flicker
/// when loading new media
/// </summary>
private IntPtr m_pBackBuffer = IntPtr.Zero;
/// <summary>
/// Flag to tell us if we have a new D3D
/// Surface available
/// </summary>
private bool m_newSurfaceAvailable;
/// <summary>
/// A weak reference of D3DRenderers that have been cloned
/// </summary>
private readonly List<WeakReference> m_clonedD3Drenderers = new List<WeakReference>();
/// <summary>
/// Backing field for the RenderOnCompositionTargetRendering flag.
/// </summary>
private bool m_renderOnCompositionTargetRendering;
/// <summary>
/// Temporary storage for the RenderOnCompositionTargetRendering flag.
/// This is used to remember the value for when the control is loaded and unloaded.
/// </summary>
private bool m_renderOnCompositionTargetRenderingTemp;
#region Stretch
public static readonly DependencyProperty StretchProperty =
DependencyProperty.Register("Stretch", typeof(Stretch), typeof(D3DRenderer),
new FrameworkPropertyMetadata(Stretch.Uniform,
new PropertyChangedCallback(OnStretchChanged)));
/// <summary>
/// Defines what rules are applied to the stretching of the video
/// </summary>
public Stretch Stretch
{
get { return (Stretch)GetValue(StretchProperty); }
set { SetValue(StretchProperty, value); }
}
private static void OnStretchChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((D3DRenderer)d).OnStretchChanged(e);
}
private void OnStretchChanged(DependencyPropertyChangedEventArgs e)
{
m_videoImage.Stretch = (Stretch) e.NewValue;
}
#endregion
#region IsRenderingEnabled
public static readonly DependencyProperty IsRenderingEnabledProperty =
DependencyProperty.Register("IsRenderingEnabled", typeof(bool), typeof(D3DRenderer),
new FrameworkPropertyMetadata(true));
/// <summary>
/// Enables or disables rendering of the video
/// </summary>
public bool IsRenderingEnabled
{
get { return (bool)GetValue(IsRenderingEnabledProperty); }
set { SetValue(IsRenderingEnabledProperty, value); }
}
#endregion
#region NaturalVideoHeight
private static readonly DependencyPropertyKey NaturalVideoHeightPropertyKey
= DependencyProperty.RegisterReadOnly("NaturalVideoHeight", typeof(int), typeof(MediaElementBase),
new FrameworkPropertyMetadata(0));
public static readonly DependencyProperty NaturalVideoHeightProperty
= NaturalVideoHeightPropertyKey.DependencyProperty;
/// <summary>
/// Gets the natural pixel height of the current media.
/// The value will be 0 if there is no video in the media.
/// </summary>
public int NaturalVideoHeight
{
get { return (int)GetValue(NaturalVideoHeightProperty); }
}
/// <summary>
/// Internal method to set the read-only NaturalVideoHeight DP
/// </summary>
protected void SetNaturalVideoHeight(int value)
{
SetValue(NaturalVideoHeightPropertyKey, value);
}
#endregion
#region NaturalVideoWidth
private static readonly DependencyPropertyKey NaturalVideoWidthPropertyKey
= DependencyProperty.RegisterReadOnly("NaturalVideoWidth", typeof(int), typeof(MediaElementBase),
new FrameworkPropertyMetadata(0));
public static readonly DependencyProperty NaturalVideoWidthProperty
= NaturalVideoWidthPropertyKey.DependencyProperty;
/// <summary>
/// Gets the natural pixel width of the current media.
/// The value will be 0 if there is no video in the media.
/// </summary>
public int NaturalVideoWidth
{
get { return (int)GetValue(NaturalVideoWidthProperty); }
}
/// <summary>
/// Internal method to set the read-only NaturalVideoWidth DP
/// </summary>
protected void SetNaturalVideoWidth(int value)
{
SetValue(NaturalVideoWidthPropertyKey, value);
}
#endregion
#region HasVideo
private static readonly DependencyPropertyKey HasVideoPropertyKey
= DependencyProperty.RegisterReadOnly("HasVideo", typeof(bool), typeof(MediaElementBase),
new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty HasVideoProperty
= HasVideoPropertyKey.DependencyProperty;
/// <summary>
/// Is true if the media contains renderable video
/// </summary>
public bool HasVideo
{
get { return (bool)GetValue(HasVideoProperty); }
}
/// <summary>
/// Internal method for setting the read-only HasVideo DP
/// </summary>
protected void SetHasVideo(bool value)
{
SetValue(HasVideoPropertyKey, value);
}
#endregion
protected D3DRenderer()
{
InitializeD3DVideo();
/* Hook into the framework events */
Loaded += D3DRenderer_Loaded;
Unloaded += D3DRenderer_Unloaded;
}
/// <summary>
/// Handler for when the D3DRenderer is unloaded
/// </summary>
private void D3DRenderer_Unloaded(object sender, RoutedEventArgs e)
{
/* Remember what the property value was */
m_renderOnCompositionTargetRenderingTemp = RenderOnCompositionTargetRendering;
/* Make sure to unhook the static event hook because we are unloading */
RenderOnCompositionTargetRendering = false;
}
/// <summary>
/// Handler for when the D3DRenderer is loaded
/// </summary>
private void D3DRenderer_Loaded(object sender, RoutedEventArgs e)
{
/* Restore the property's value */
RenderOnCompositionTargetRendering = m_renderOnCompositionTargetRenderingTemp;
}
/// <summary>
/// Initializes the D3DRenderer control
/// </summary>
protected virtual void InitializeD3DVideo()
{
if (m_videoImage != null)
return;
/* Create our Image and it's D3DImage source */
m_videoImage = new Image();
m_d3dImage = new D3DImage();
/* We hook into this event to handle when a D3D device is lost */
D3DImage.IsFrontBufferAvailableChanged += D3DImage_IsFrontBufferAvailableChanged;
/* Set our default stretch value of our video */
m_videoImage.Stretch = (Stretch)StretchProperty.DefaultMetadata.DefaultValue;
var activeProfiles = XElement.Load(workingDirectory + "ProfilesDisplay.xml");
var a = activeProfiles.Element("_nABC").Value;
/* Our source of the video image is the D3DImage */
m_videoImage.Source = D3DImage;
/* Register the Image as a visual child */
AddVisualChild(m_videoImage);
}
/// <summary>
/// This should only fire when a D3D device is lost
/// </summary>
private void D3DImage_IsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (!D3DImage.IsFrontBufferAvailable)
return;
/* Flag that we have a new surface, even
* though we really don't */
m_newSurfaceAvailable = true;
/* Force feed the D3DImage the Surface pointer */
SetBackBufferInternal(m_pBackBuffer);
}
protected override Size MeasureOverride(Size availableSize)
{
m_videoImage.Measure(availableSize);
return m_videoImage.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
m_videoImage.Arrange(new Rect(finalSize));
return finalSize;
}
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
protected override Visual GetVisualChild(int index)
{
if (index > 0)
throw new IndexOutOfRangeException();
return m_videoImage;
}
protected D3DImage D3DImage
{
get
{
return m_d3dImage;
}
}
protected Image VideoImage
{
get
{
return m_videoImage;
}
}
/// <summary>
/// Renders the video with WPF's rendering using the CompositionTarget.Rendering event
/// </summary>
protected bool RenderOnCompositionTargetRendering
{
get
{
return m_renderOnCompositionTargetRendering;
}
set
{
/* If it is being set to true and it was previously false
* then hook into the event */
if (value && !m_renderOnCompositionTargetRendering)
CompositionTarget.Rendering += CompositionTarget_Rendering;
else if(!value)
CompositionTarget.Rendering -= CompositionTarget_Rendering;
m_renderOnCompositionTargetRendering = value;
m_renderOnCompositionTargetRenderingTemp = value;
}
}
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
InternalInvalidateVideoImage();
}
/// <summary>
/// Used as a clone for a D3DRenderer
/// </summary>
private class ClonedD3DRenderer : D3DRenderer
{}
/// <summary>
/// Creates a clone of the D3DRenderer. This is a work for the visual
/// brush not working cross-threaded
/// </summary>
/// <returns></returns>
public D3DRenderer CloneD3DRenderer()
{
var renderer = new ClonedD3DRenderer();
lock(m_clonedD3Drenderers)
{
m_clonedD3Drenderers.Add(new WeakReference(renderer));
}
renderer.SetBackBuffer(m_pBackBuffer);
return renderer;
}
/// <summary>
/// Cleans up any dead references we may have to any cloned renderers
/// </summary>
private void CleanZombieRenderers()
{
lock(m_clonedD3Drenderers)
{
var deadObjects = new List<WeakReference>();
for (int i = 0; i < m_clonedD3Drenderers.Count; i++)
{
if (!m_clonedD3Drenderers[i].IsAlive)
deadObjects.Add(m_clonedD3Drenderers[i]);
}
foreach(var deadGuy in deadObjects)
{
m_clonedD3Drenderers.Remove(deadGuy);
}
}
}
/// <summary>
/// Configures D3DImage with a new surface. The back buffer is
/// not set until we actually receive a frame, this way we
/// can avoid a black flicker between media changes
/// </summary>
/// <param name="backBuffer">The unmanaged pointer to the Direct3D Surface</param>
protected void SetBackBuffer(IntPtr backBuffer)
{
/* We only do this if target rendering is enabled because we must use an Invoke
* instead of a BeginInvoke to keep the Surfaces in sync and Invoke could be dangerous
* in other situations */
if(RenderOnCompositionTargetRendering)
{
if (!D3DImage.Dispatcher.CheckAccess())
{
D3DImage.Dispatcher.Invoke((Action)(() => SetBackBuffer(backBuffer)), DispatcherPriority.Render);
return;
}
}
/* Flag a new surface */
m_newSurfaceAvailable = true;
m_pBackBuffer = backBuffer;
/* Make a special case for target rendering */
if (RenderOnCompositionTargetRendering)
{
SetBackBufferInternal(m_pBackBuffer);
}
SetBackBufferForClones();
}
/// <summary>
/// Sets the backbuffer for any cloned D3DRenderers
/// </summary>
private void SetBackBufferForClones()
{
lock (m_clonedD3Drenderers)
{
CleanZombieRenderers();
foreach (var rendererRef in m_clonedD3Drenderers)
{
var renderer = rendererRef.Target as D3DRenderer;
if (renderer != null)
renderer.SetBackBuffer(m_pBackBuffer);
}
}
}
/// <summary>
/// Configures D3DImage with a new surface. This happens immediately
/// </summary>
private void SetBackBufferInternal(IntPtr backBuffer)
{
/* Do nothing if we don't have a new surface available */
if (!m_newSurfaceAvailable)
return;
if(!D3DImage.Dispatcher.CheckAccess())
{
D3DImage.Dispatcher.BeginInvoke((Action)(() => SetBackBufferInternal(backBuffer)));
return;
}
/* We have this around a try/catch just in case we
* lose the device and our Surface is invalid. The
* try/catch may not be needed, but testing needs
* to take place before it's removed */
try
{
D3DImage.Lock();
D3DImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, backBuffer);
D3DImage.Unlock();
SetNaturalWidthHeight();
}
catch (Exception)
{ }
/* Clear our flag, so this won't be ran again
* until a new surface is sent */
m_newSurfaceAvailable = false;
}
private void SetNaturalWidthHeight()
{
SetNaturalVideoHeight(m_d3dImage.PixelHeight);
SetNaturalVideoWidth(m_d3dImage.PixelWidth);
}
protected void InvalidateVideoImage()
{
if (!m_renderOnCompositionTargetRendering)
InternalInvalidateVideoImage();
}
/// <summary>
/// Invalidates the entire Direct3D image, notifying WPF to redraw
/// </summary>
protected void InternalInvalidateVideoImage()
{
/* Ensure we run on the correct Dispatcher */
if(!D3DImage.Dispatcher.CheckAccess())
{
D3DImage.Dispatcher.BeginInvoke((Action)(() => InvalidateVideoImage()));
return;
}
/* If there is a new Surface to set,
* this method will do the trick */
SetBackBufferInternal(m_pBackBuffer);
/* Only render the video image if possible, or if IsRenderingEnabled is true */
if (D3DImage.IsFrontBufferAvailable && IsRenderingEnabled && m_pBackBuffer != IntPtr.Zero)
{
try
{
/* Invalidate the entire image */
D3DImage.Lock();
D3DImage.AddDirtyRect(new Int32Rect(0, /* Left */
0, /* Top */
D3DImage.PixelWidth, /* Width */
D3DImage.PixelHeight /* Height */));
D3DImage.Unlock();
}
catch
{ }
}
/* Invalidate all of our cloned D3DRenderers */
InvalidateClonedVideoImages();
}
/// <summary>
/// Invalidates any possible cloned renderer we may have
/// </summary>
private void InvalidateClonedVideoImages()
{
lock(m_clonedD3Drenderers)
{
CleanZombieRenderers();
foreach(var rendererRef in m_clonedD3Drenderers)
{
var renderer = rendererRef.Target as D3DRenderer;
if(renderer != null)
renderer.InvalidateVideoImage();
}
}
}
}
}
In the InitializeD3DVideo() function, I try to read a xml file from a location from the harddisk. But it can't be done.
Is it that the d3drenderer.cs cannot read from a xml file? I just want to read a value from _nABC of the xml file.
This has nothing to do with the D3DRenderer.
The path to the XML file is wrong. There is a '/' (or '\') missing between the workingDirectory and the + "ProfilesDisplay.xml".
If you don't know your path strings, the best method to combine these is System.IO.Path.Combine.
In my xamarin forms app, On main page a list of items are displayed. A checkbox image is made visible when user selects an item from the list. Image visibility is toggled when user taps on an item, to give user the impression that item has been selected.
This functionality was working fine in all platforms i.e. iOs, Android, WP8.1. But once when I upgraded my xamarin forms version from 1.5X to 2.X, The checkbox image stoped appearing in windows phone when user select an item. Although in iOs and android its working fine.
If I downgrade the xamarin forms back to version 1.5, functionality works fine.
I also discovered that when app starts in windows phone, If we rotate the screen horizontally, the checkbox image starts functioning again. But that's just a hack!!!
For the checkbox functionality I have used the following code -
/// <summary>
/// A selectable cross-platform cell
/// </summary>
public class CustomSelectableCell : ViewCell
{
private Grid _gridLayout;
private Label _text;
private Image _image;
/// <summary>
/// The BindableProperty
/// </summary>
public static readonly BindableProperty CommandProperty = BindableProperty.Create<CustomSelectableCell, Command>(i => i.Command, default(Command));
/// <summary>
/// Gets/Sets the command which gets executed on tapped
/// </summary>
public Command Command
{
get
{
return (Command)this.GetValue(CommandProperty);
}
set
{
this.SetValue(CommandProperty, value);
}
}
/// <summary>
/// Then BindableProperty
/// </summary>
public static readonly BindableProperty AutoSelectProperty = BindableProperty.Create<CustomSelectableCell, bool>(i => i.AutoSelect, true, BindingMode.TwoWay);
/// <summary>
/// Gets/Sets if the cell will automatically select the itself by tapping
/// </summary>
public bool AutoSelect
{
get
{
return (bool)this.GetValue(AutoSelectProperty);
}
set
{
this.SetValue(AutoSelectProperty, value);
}
}
/// <summary>
/// The BindableProperty
/// </summary>
public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create<CustomSelectableCell, bool>(i => i.IsSelected, default(bool), BindingMode.TwoWay, null, IsSelectedChanged);
/// <summary>
/// Gets/Sets if the cell is in selected state or not
/// </summary>
public bool IsSelected
{
get
{
return (bool)this.GetValue(IsSelectedProperty);
}
set
{
this.SetValue(IsSelectedProperty, value);
}
}
/// <summary>
/// The BindableProperty
/// </summary>
public static readonly BindableProperty TextProperty = BindableProperty.Create<CustomSelectableCell, string>(i => i.Text, default(string), BindingMode.TwoWay, null, TextChanged);
/// <summary>
/// Gets/Sets the text of the cell
/// </summary>
public string Text
{
get
{
return (string)this.GetValue(TextProperty);
}
set
{
this.SetValue(TextProperty, value);
}
}
/// <summary>
/// Creates a new instance of <c>CustomSelectableCell</c>
/// </summary>
public CustomSelectableCell()
{
this._gridLayout = new Grid
{
RowDefinitions =
{
new RowDefinition
{
Height = new GridLength(1, GridUnitType.Star)
}
},
ColumnDefinitions =
{
new ColumnDefinition
{
Width = new GridLength(1, GridUnitType.Star)
},
new ColumnDefinition
{
Width = new GridLength(1, GridUnitType.Auto)
}
}
};
this._image = new Image
{
Source = "ic_done_white_24dp.png",
IsVisible = false
};
this._text = new Label
{
Style = Device.Styles.ListItemTextStyle
};
this._gridLayout.Children.Add(this._text, 0, 0);
this._gridLayout.Children.Add(this._image, 1, 0);
this.View = this._gridLayout;
}
/// <summary>
/// Raised when <c>IsSelected</c> changed
/// </summary>
/// <param name="obj">The cell</param>
/// <param name="oldValue">Old value</param>
/// <param name="newValue">New value</param>
private static void IsSelectedChanged(BindableObject obj, bool oldValue, bool newValue)
{
var cell = obj as CustomSelectableCell;
if (cell != null)
{
cell._image.IsVisible = newValue;
}
}
/// <summary>
/// Raised when the <c>Text</c> changed
/// </summary>
/// <param name="obj">The cell</param>
/// <param name="oldValue">Old value</param>
/// <param name="newValue">New value</param>
private static void TextChanged(BindableObject obj, string oldValue, string newValue)
{
var cell = obj as CustomSelectableCell;
if (cell != null)
{
cell._text.Text = newValue;
}
}
/// <summary>
/// Auto Select on tapped
/// </summary>
protected override void OnTapped()
{
base.OnTapped();
if (this.AutoSelect)
{
this.IsSelected = !this.IsSelected;
}
if (this.Command != null)
{
if (this.Command.CanExecute(this))
{
this.Command.Execute(this);
}
}
}
}
Could someone please help it get resolved??? Any help would be greatly appreciated. Please let me know if you need more information.
Thanks.
Jkm
I'm new to Silverlight and MVVM and I'm trying to change Button.IsEnabled of one xaml from another xaml inside the app.
I tried to create a class but I'm not sure I'm using it correctly...
using System.ComponentModel;
namespace Reporting.Models
{
public class DataSourceValidation : INotifyPropertyChanged
{
private bool _isSaveEnabled;
public bool IsSaveEnabled
{
get
{
return _isSaveEnabled;
}
set
{
_isSaveEnabled = value;
RaisedPropertyChanged("IsSaveEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisedPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
I'd be very grateful for an explanation and example. I've been looking but its not quite clicking with me on how you bind the control to a model and then change that value from another xaml.cs... Provided I am even thinking about this the correct way.
If I need to provide more information just ask. Thanks for your time.
If you just wan't to get it done quickly (no good solution) then you can make this happen using static properties:
In ExampleA.xaml.cs create the following property:
public static ExampleA CurrentInstance { get; private set; }
and in the constructor of ExampleA (I will assume that the name of the class is ExampleA):
public ExampleA()
{
this.InitializeComponent();
CurrentInstance = this;
}
Then in your ExampleB class LostFocus method:
if (ExampleA.CurrentInstance != null)
{
var button = ExampleA.CurrentInstance.FindName("OKButton") as Button;
if (button != null)
{
button.IsEnabled = true;
}
}
On the other hand, I don't know how your classes are structured and how these XAML files are related, but a good universal solution would be by using the Event Aggregator pattern:
I am using here a slightly modified version of the EventAggregator class from the Caliburn.Micro MVVM framework:
namespace Caliburn.Micro
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
/// <summary>
/// A marker interface for classes that subscribe to messages.
/// </summary>
public interface IHandle { }
/// <summary>
/// Denotes a class which can handle a particular type of message.
/// </summary>
/// <typeparam name = "TMessage">The type of message to handle.</typeparam>
public interface IHandle<TMessage> : IHandle
{
/// <summary>
/// Handles the message.
/// </summary>
/// <param name = "message">The message.</param>
void Handle(TMessage message);
}
/// <summary>
/// Enables loosely-coupled publication of and subscription to events.
/// </summary>
public interface IEventAggregator
{
/// <summary>
/// Gets or sets the default publication thread marshaller.
/// </summary>
/// <value>
/// The default publication thread marshaller.
/// </value>
Action<System.Action> PublicationThreadMarshaller { get; set; }
/// <summary>
/// Subscribes an instance to all events declared through implementations of <see cref = "IHandle{T}" />
/// </summary>
/// <param name = "instance">The instance to subscribe for event publication.</param>
void Subscribe(object instance);
/// <summary>
/// Unsubscribes the instance from all events.
/// </summary>
/// <param name = "instance">The instance to unsubscribe.</param>
void Unsubscribe(object instance);
/// <summary>
/// Publishes a message.
/// </summary>
/// <param name = "message">The message instance.</param>
/// <remarks>
/// Uses the default thread marshaller during publication.
/// </remarks>
void Publish(object message);
/// <summary>
/// Publishes a message.
/// </summary>
/// <param name = "message">The message instance.</param>
/// <param name = "marshal">Allows the publisher to provide a custom thread marshaller for the message publication.</param>
void Publish(object message, Action<System.Action> marshal);
}
/// <summary>
/// Enables loosely-coupled publication of and subscription to events.
/// </summary>
public class EventAggregator : IEventAggregator
{
/// <summary>
/// The singleton instance.
/// </summary>
public static EventAggregator Instance = new EventAggregator();
/// <summary>
/// The default thread marshaller used for publication;
/// </summary>
public static Action<System.Action> DefaultPublicationThreadMarshaller = action => action();
readonly List<Handler> handlers = new List<Handler>();
/// <summary>
/// Initializes a new instance of the <see cref = "EventAggregator" /> class.
/// </summary>
public EventAggregator()
{
PublicationThreadMarshaller = DefaultPublicationThreadMarshaller;
}
/// <summary>
/// Gets or sets the default publication thread marshaller.
/// </summary>
/// <value>
/// The default publication thread marshaller.
/// </value>
public Action<System.Action> PublicationThreadMarshaller { get; set; }
/// <summary>
/// Subscribes an instance to all events declared through implementations of <see cref = "IHandle{T}" />
/// </summary>
/// <param name = "instance">The instance to subscribe for event publication.</param>
public virtual void Subscribe(object instance)
{
lock (handlers)
{
if (handlers.Any(x => x.Matches(instance)))
return;
handlers.Add(new Handler(instance));
}
}
/// <summary>
/// Unsubscribes the instance from all events.
/// </summary>
/// <param name = "instance">The instance to unsubscribe.</param>
public virtual void Unsubscribe(object instance)
{
lock (handlers)
{
var found = handlers.FirstOrDefault(x => x.Matches(instance));
if (found != null)
handlers.Remove(found);
}
}
/// <summary>
/// Publishes a message.
/// </summary>
/// <param name = "message">The message instance.</param>
/// <remarks>
/// Does not marshall the the publication to any special thread by default.
/// </remarks>
public virtual void Publish(object message)
{
Publish(message, PublicationThreadMarshaller);
}
/// <summary>
/// Publishes a message.
/// </summary>
/// <param name = "message">The message instance.</param>
/// <param name = "marshal">Allows the publisher to provide a custom thread marshaller for the message publication.</param>
public virtual void Publish(object message, Action<System.Action> marshal)
{
Handler[] toNotify;
lock (handlers)
toNotify = handlers.ToArray();
marshal(() =>
{
var messageType = message.GetType();
var dead = toNotify
.Where(handler => !handler.Handle(messageType, message))
.ToList();
if (dead.Any())
{
lock (handlers)
{
foreach (var handler in dead)
{
handlers.Remove(handler);
}
}
}
});
}
protected class Handler
{
readonly WeakReference reference;
readonly Dictionary<Type, MethodInfo> supportedHandlers = new Dictionary<Type, MethodInfo>();
public Handler(object handler)
{
reference = new WeakReference(handler);
var interfaces = handler.GetType().GetInterfaces()
.Where(x => typeof(IHandle).IsAssignableFrom(x) && x.IsGenericType);
foreach (var #interface in interfaces)
{
var type = #interface.GetGenericArguments()[0];
var method = #interface.GetMethod("Handle");
supportedHandlers[type] = method;
}
}
public bool Matches(object instance)
{
return reference.Target == instance;
}
public bool Handle(Type messageType, object message)
{
var target = reference.Target;
if (target == null)
return false;
foreach (var pair in supportedHandlers)
{
if (pair.Key.IsAssignableFrom(messageType))
{
pair.Value.Invoke(target, new[] { message });
return true;
}
}
return true;
}
}
}
}
Copy this class in your project.
Then create a simple message class which will represent a message to any listener that a button needs to be enabled:
public class EnableButtonMessage
{
}
Your ExampleA class then needs to implement the IHandle<EnableButtonMessage> interface (in the constructor you need to subscribe that this class is receiving some kind of messages, and the Handle method executes when an actual message of a registered type is received in which you then enable the button):
public sealed class ExampleA : UserControl, IHandle<EnableButtonMessage>
{
...
public ExampleA()
{
this.InitializeComponent();
EventAggregator.Instance.Subscribe(this);
}
...
public void Handle(EnableButtonMessage message)
{
this.OKButton.IsEnabled = true;
}
...
}
In ExampleB class' LostFocus method you just simply call to change your button property:
EventAggregator.Instance.Publish(new EnableButtonMessage());
This is a simple implementation of the EventAggregator pattern and you can then use if for any messaging between any entities inside your application globally. This is a powerful pattern and when used alongside MVVM can make your life a lot easier.
Does someone know a way to keep the visual selection state of a selected text in a RichEditBox? I want to add some basic text editing to my Windows 8.1 App but every time I select a text and click on another UI Element within the app, the RichEditBox hides the selection.
I already tried to register the unfocus event an set the selection range again but unfortunately this has no effect.
I also tried to draw my own rect over the Text using
richEdit.Document.Selection.GetRect(PointOptions.ClientCoordinates,out selectionRect, out hitCount );
This works as long as only some text within a single line is selected. If the selection is multilined I only get the top-left of and the bottom-right position of the selected Text. It seems like these are the mouse Positions where the selection where started and where it ended.
Are there any other ways keep to the selected text visibible when the RichEditBox is unfocused.
I found another workaround. Just set the selection Background when the RichEditBox is unfocused. But Jerry's Post gave me the Inspiration to this solution. Seems like this way was to simple to find it a first place:
private void RichEditOnGotFocus(object sender, RoutedEventArgs routedEventArgs)
{
ITextSelection selectedText = richEdit.Document.Selection;
if (selectedText != null)
{
richEdit.Document.Selection.SetRange(_selectionStart, _selectionEnd);
selectedText.CharacterFormat.BackgroundColor = Colors.White;
}
}
private void RichEditOnLostFocus(object sender, RoutedEventArgs routedEventArgs)
{
_selectionEnd = richEdit.Document.Selection.EndPosition;
_selectionStart = richEdit.Document.Selection.StartPosition;
ITextSelection selectedText = richEdit.Document.Selection;
if (selectedText != null)
{
selectedText.CharacterFormat.BackgroundColor = Colors.Gray;
}
}
In the WinRT Toolkit, there's a HighlightBehavior that doesn't directly solve your problem, but might offer you a solution that is acceptable.
http://winrtxamltoolkit.codeplex.com/SourceControl/latest#WinRTXamlToolkit/WinRTXamlToolkit.Shared/Controls/Behaviors/HighlightBehavior.cs
public class HighlightBehavior : Behavior<TextBlock>
{
#region SearchString
/// <summary>
/// SearchString Dependency Property
/// </summary>
public static readonly DependencyProperty SearchStringProperty =
DependencyProperty.Register(
"SearchString",
typeof(string),
typeof(HighlightBehavior),
new PropertyMetadata(null, OnSearchStringChanged));
/// <summary>
/// Gets or sets the SearchString property. This dependency property
/// indicates the search string to highlight in the associated TextBlock.
/// </summary>
public string SearchString
{
get { return (string)GetValue(SearchStringProperty); }
set { SetValue(SearchStringProperty, value); }
}
/// <summary>
/// Handles changes to the SearchString property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnSearchStringChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (HighlightBehavior)d;
string oldSearchString = (string)e.OldValue;
string newSearchString = target.SearchString;
target.OnSearchStringChanged(oldSearchString, newSearchString);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes
/// to the SearchString property.
/// </summary>
/// <param name="oldSearchString">The old SearchString value</param>
/// <param name="newSearchString">The new SearchString value</param>
private void OnSearchStringChanged(
string oldSearchString, string newSearchString)
{
UpdateHighlight();
}
#endregion
#region IsCaseSensitive
/// <summary>
/// IsCaseSensitive Dependency Property
/// </summary>
public static readonly DependencyProperty IsCaseSensitiveProperty =
DependencyProperty.Register(
"IsCaseSensitive",
typeof(bool),
typeof(HighlightBehavior),
new PropertyMetadata(false, OnIsCaseSensitiveChanged));
/// <summary>
/// Gets or sets the IsCaseSensitive property. This dependency property
/// indicates whether the highlight behavior is case sensitive.
/// </summary>
public bool IsCaseSensitive
{
get { return (bool)GetValue(IsCaseSensitiveProperty); }
set { SetValue(IsCaseSensitiveProperty, value); }
}
/// <summary>
/// Handles changes to the IsCaseSensitive property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnIsCaseSensitiveChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (HighlightBehavior)d;
bool oldIsCaseSensitive = (bool)e.OldValue;
bool newIsCaseSensitive = target.IsCaseSensitive;
target.OnIsCaseSensitiveChanged(oldIsCaseSensitive, newIsCaseSensitive);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes
/// to the IsCaseSensitive property.
/// </summary>
/// <param name="oldIsCaseSensitive">The old IsCaseSensitive value</param>
/// <param name="newIsCaseSensitive">The new IsCaseSensitive value</param>
private void OnIsCaseSensitiveChanged(
bool oldIsCaseSensitive, bool newIsCaseSensitive)
{
UpdateHighlight();
}
#endregion
#region HighlightTemplate
/// <summary>
/// HighlightTemplate Dependency Property
/// </summary>
public static readonly DependencyProperty HighlightTemplateProperty =
DependencyProperty.Register(
"HighlightTemplate",
typeof(DataTemplate),
typeof(HighlightBehavior),
new PropertyMetadata(null, OnHighlightTemplateChanged));
/// <summary>
/// Gets or sets the HighlightTemplate property. This dependency property
/// indicates the template to use to generate the highlight Run inlines.
/// </summary>
public DataTemplate HighlightTemplate
{
get { return (DataTemplate)GetValue(HighlightTemplateProperty); }
set { SetValue(HighlightTemplateProperty, value); }
}
/// <summary>
/// Handles changes to the HighlightTemplate property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnHighlightTemplateChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (HighlightBehavior)d;
DataTemplate oldHighlightTemplate = (DataTemplate)e.OldValue;
DataTemplate newHighlightTemplate = target.HighlightTemplate;
target.OnHighlightTemplateChanged(oldHighlightTemplate, newHighlightTemplate);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes
/// to the HighlightTemplate property.
/// </summary>
/// <param name="oldHighlightTemplate">The old HighlightTemplate value</param>
/// <param name="newHighlightTemplate">The new HighlightTemplate value</param>
private void OnHighlightTemplateChanged(
DataTemplate oldHighlightTemplate, DataTemplate newHighlightTemplate)
{
UpdateHighlight();
}
#endregion
#region HighlightBrush
/// <summary>
/// HighlightBrush Dependency Property
/// </summary>
public static readonly DependencyProperty HighlightBrushProperty =
DependencyProperty.Register(
"HighlightBrush",
typeof(Brush),
typeof(HighlightBehavior),
new PropertyMetadata(new SolidColorBrush(Colors.Red), OnHighlightBrushChanged));
/// <summary>
/// Gets or sets the HighlightBrush property. This dependency property
/// indicates the brush to use to highlight the found instances of the search string.
/// </summary>
/// <remarks>
/// Note that the brush is ignored if HighlightTemplate is specified
/// </remarks>
public Brush HighlightBrush
{
get { return (Brush)GetValue(HighlightBrushProperty); }
set { SetValue(HighlightBrushProperty, value); }
}
/// <summary>
/// Handles changes to the HighlightBrush property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnHighlightBrushChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (HighlightBehavior)d;
Brush oldHighlightBrush = (Brush)e.OldValue;
Brush newHighlightBrush = target.HighlightBrush;
target.OnHighlightBrushChanged(oldHighlightBrush, newHighlightBrush);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes
/// to the HighlightBrush property.
/// </summary>
/// <param name="oldHighlightBrush">The old HighlightBrush value</param>
/// <param name="newHighlightBrush">The new HighlightBrush value</param>
private void OnHighlightBrushChanged(
Brush oldHighlightBrush, Brush newHighlightBrush)
{
UpdateHighlight();
}
#endregion
private PropertyChangeEventSource<string> _textChangeEventSource;
/// <summary>
/// Called after the behavior is attached to an AssociatedObject.
/// </summary>
/// <remarks>
/// Override this to hook up functionality to the AssociatedObject.
/// </remarks>
protected override void OnAttached()
{
UpdateHighlight();
_textChangeEventSource = new PropertyChangeEventSource<string>(this.AssociatedObject, "Text", BindingMode.OneWay);
_textChangeEventSource.ValueChanged += TextChanged;
base.OnAttached();
}
/// <summary>
/// Called when the behavior is being detached from its AssociatedObject, but
/// before it has actually occurred.
/// </summary>
/// <remarks>
/// Override this to unhook functionality from the AssociatedObject.
/// </remarks>
protected override void OnDetaching()
{
ClearHighlight();
_textChangeEventSource.ValueChanged -= TextChanged;
_textChangeEventSource = null;
base.OnDetaching();
}
private void TextChanged(object sender, string s)
{
UpdateHighlight();
}
/// <summary>
/// Updates the highlight.
/// </summary>
public void UpdateHighlight()
{
if (this.AssociatedObject == null ||
string.IsNullOrEmpty(this.AssociatedObject.Text) ||
string.IsNullOrEmpty(this.SearchString))
{
ClearHighlight();
return;
}
var txt = this.AssociatedObject.Text;
var searchTxt = this.SearchString;
var processedCharacters = 0;
this.AssociatedObject.Inlines.Clear();
int pos;
while ((pos = txt.IndexOf(
searchTxt,
processedCharacters,
this.IsCaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) >= 0)
{
if (pos > processedCharacters)
{
var run = new Run
{
Text =
txt.Substring(
processedCharacters, pos - processedCharacters)
};
this.AssociatedObject.Inlines.Add(run);
}
Run highlight;
var highlightText = txt.Substring(pos, searchTxt.Length);
if (this.HighlightTemplate == null)
{
highlight =
new Run
{
Text = highlightText,
Foreground = this.HighlightBrush
};
}
else
{
highlight = (Run)this.HighlightTemplate.LoadContent();
highlight.Text = highlightText;
}
this.AssociatedObject.Inlines.Add(highlight);
processedCharacters = pos + searchTxt.Length;
}
if (processedCharacters < txt.Length)
{
var run = new Run
{
Text =
txt.Substring(
processedCharacters, txt.Length - processedCharacters)
};
this.AssociatedObject.Inlines.Add(run);
}
}
/// <summary>
/// Clears the highlight.
/// </summary>
public void ClearHighlight()
{
if (this.AssociatedObject == null)
{
return;
}
var text = this.AssociatedObject.Text;
this.AssociatedObject.Inlines.Clear();
this.AssociatedObject.Inlines.Add(new Run{Text = text});
}
}
public abstract class Behavior<T> : Behavior where T : DependencyObject
{
#region Behavior() - CTOR
/// <summary>
/// Initializes a new instance of the <see cref="Behavior<T>"/> class.
/// </summary>
protected Behavior()
{
_associatedType = typeof(T);
}
#endregion
#region AssociatedObject
/// <summary>
/// Gets the object to which this <see cref="Behavior<T>" /> is attached.
/// </summary>
public new T AssociatedObject
{
get
{
return (T)_associatedObject;
}
internal set
{
_associatedObject = value;
}
}
#endregion
}
Best of luck!
I'm curious if there is a simple way to bind properties of a class to individual items of a CheckedListBox. There are several components out there (Telerik/DevExpress) that provide PropertyEditor grids, but I'm looking to do it in a CheckedListBox.
IE:
public class MyItem
{
public bool Property1
{
get;
set;
}
public bool Property2
{
get;
set;
}
public bool Property3
{
get;
set;
}
}
And when adding items to the CheckedListBox, I'd like to have some sort of method that lets me do:
this.AddCheckListBoxItem("Property A", this.myItem.Property1);
this.AddCheckListBoxItem("Property B", this.myItem.Property2);
this.AddCheckListBoxItem("Property C", this.myItem.Property3);
the first parameter being the display name within the CheckedListBox.
Then throughout any changes to the checkstate, the bool values would automatically be updated without further code.
Currently there isn't any easy/simple way to get the functionallity you are looking for.
As in the comments the nearest solution I can think of would be to use reflection.
If you manage to build a helper class that has this functionallity, please post here as I would also find that usefull.
I've created a solution using reflection as suggested by #Jethro. I used generics in the class definition, but I didn't implement anything that uses them yet. To summarize, basically using reflection, I've bound a single object and it's boolean properties to individual items within a standard CheckedListBox. It's nice as it doesn't require having to code to set the property values and get them when saving data, as the reflection takes care of this.
I created a Form, and added a CheckedListBox to it, and a button. Here's the code side.
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
/// <summary>
/// The main form.
/// </summary>
public partial class Form1 : Form
{
/// <summary>
/// The checked list box property helper.
/// </summary>
private CheckedListBoxPropertyHelper<MyItem> helper;
/// <summary>
/// My object to bind to the checked list box.
/// </summary>
private MyItem myObjectDataSource;
/// <summary>
/// Initializes a new instance of the <see cref="Form1"/> class.
/// </summary>
public Form1()
{
this.InitializeComponent();
this.myObjectDataSource = new MyItem();
this.helper = new CheckedListBoxPropertyHelper<MyItem>(this.checkedListBox1, this.myObjectDataSource, true);
this.helper.AddCheckListBoxItem(new CheckedPropertyItem("Property One", "Property1"));
this.helper.AddCheckListBoxItem(new CheckedPropertyItem("Property Two", "Property2"));
this.helper.AddCheckListBoxItem(new CheckedPropertyItem("Property Three", "Property3"));
}
/// <summary>
/// Handles the Click event of the Button1 control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void Button1_Click(object sender, EventArgs e)
{
// In the constructor
// if we instantiated: this.helper = new CheckedListBoxPropertyHelper<MyItem>(this.checkedListBox1, this.myObjectDataSource, true);
// as: this.helper = new CheckedListBoxPropertyHelper<MyItem>(this.checkedListBox1, this.myObjectDataSource, false);
// changing the last bindImmediate property to false, the changes to the checkboxes wouldn't take effect immediately
// on the underlying object, you need to call this.helper.CommitChanges() at which point the changes
// will be made to the datasource object.
// this.helper.CommitChanges();
Console.WriteLine(this.myObjectDataSource.Property1.ToString());
Console.WriteLine(this.myObjectDataSource.Property2.ToString());
Console.WriteLine(this.myObjectDataSource.Property3.ToString());
}
}
/// <summary>
/// My item.
/// </summary>
public class MyItem
{
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MyItem"/> is property1.
/// </summary>
/// <value><c>true</c> if property1; otherwise, <c>false</c>.</value>
public bool Property1
{
get;
set;
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MyItem"/> is property2.
/// </summary>
/// <value><c>true</c> if property2; otherwise, <c>false</c>.</value>
public bool Property2
{
get;
set;
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MyItem"/> is property3.
/// </summary>
/// <value><c>true</c> if property3; otherwise, <c>false</c>.</value>
public bool Property3
{
get;
set;
}
}
/// <summary>
/// The checked list box property helper. This binds datasource properties to checkedlistbox items.
/// </summary>
public class CheckedListBoxPropertyHelper<T> where T : class
{
/// <summary>
/// The checked list box.
/// </summary>
private CheckedListBox checkedListBox;
/// <summary>
/// The property info.
/// </summary>
private PropertyInfo[] PropertyInfo;
/// <summary>
/// Initializes a new instance of the <see cref="CheckedListBoxPropertyHelper"/> class.
/// </summary>
/// <param name="checkedListBox">The checked list box.</param>
/// <param name="dataSource">The data source.</param>
/// <param name="bindImmediate">if set to <c>true</c> [bind immediate].</param>
public CheckedListBoxPropertyHelper(CheckedListBox checkedListBox, T dataSource, bool bindImmediate)
{
this.checkedListBox = checkedListBox;
this.DataSource = dataSource;
this.PropertyInfo = this.DataSource.GetType().GetProperties();
this.BindImmediate = bindImmediate;
this.checkedListBox.ItemCheck += new ItemCheckEventHandler(CheckedListBox_ItemCheck);
}
/// <summary>
/// The data source.
/// </summary>
public T DataSource
{
get;
private set;
}
/// <summary>
/// Gets or sets a value indicating whether to [bind immediately] to the datasource object.
/// </summary>
/// <value><c>true</c> if [bind immediately]; otherwise, <c>false</c>.</value>
public bool BindImmediate
{
get;
set;
}
/// <summary>
/// Commits the changes.
/// </summary>
public void CommitChanges()
{
if (!this.BindImmediate)
{
for (int i = 0; i < this.checkedListBox.Items.Count; i++)
{
CheckedPropertyItem checkedItem = this.checkedListBox.Items[i] as CheckedPropertyItem;
this.SetChecked(this.checkedListBox.GetItemChecked(i), checkedItem);
}
}
else
{
throw new InvalidOperationException("You cannot commit changes when bind immediate is on.");
}
}
/// <summary>
/// Adds the check list box item.
/// </summary>
/// <param name="item">The item.</param>
public void AddCheckListBoxItem(CheckedPropertyItem item)
{
PropertyInfo info = this.GetPropertyInfo(item.PropertyName);
bool isChecked = false;
if (info != null)
{
isChecked = (bool)info.GetValue(this.DataSource, null);
}
this.checkedListBox.Items.Add(item, isChecked);
}
/// <summary>
/// Handles the ItemCheck event of the CheckedListBox control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Windows.Forms.ItemCheckEventArgs"/> instance containing the event data.</param>
private void CheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (BindImmediate)
{
CheckedListBox clb = sender as CheckedListBox;
CheckedPropertyItem checkedItem = clb.Items[e.Index] as CheckedPropertyItem;
this.SetChecked(
e.NewValue == CheckState.Checked ? true : false,
checkedItem);
}
}
/// <summary>
/// Sets the checked.
/// </summary>
/// <param name="isChecked">if set to <c>true</c> [is checked].</param>
/// <param name="clb">The CLB.</param>
private void SetChecked(bool isChecked, CheckedPropertyItem item)
{
PropertyInfo info = this.GetPropertyInfo(item.PropertyName);
if (info.CanWrite)
{
info.SetValue(this.DataSource, isChecked, null);
}
}
/// <summary>
/// Gets the property info.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
private PropertyInfo GetPropertyInfo(string propertyName)
{
return this.PropertyInfo
.Where(c => c.Name == propertyName)
.SingleOrDefault();
}
}
/// <summary>
/// Checked Property Item binding.
/// </summary>
public class CheckedPropertyItem
{
/// <summary>
/// Initializes a new instance of the <see cref="CheckedPropertyItem"/> class.
/// </summary>
/// <param name="title">The title.</param>
/// <param name="propertyName">Name of the property.</param>
public CheckedPropertyItem(string title, string propertyName)
{
this.Title = title;
this.PropertyName = propertyName;
}
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public string Title
{
get;
private set;
}
/// <summary>
/// Gets the name of the property.
/// </summary>
public string PropertyName
{
get;
private set;
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
public override string ToString()
{
return this.Title;
}
}