IoC Container is a framework for implementing automatic dependency injection. before I explain IoC Container first lets know what is dependency injection
Dependency Injection
It is a design pattern used to implement IoC. It allows the creation of dependent objects outside of a class and provides those objects to a class through different ways.
The Dependency Injection pattern involves three things which are client, service and the injector so, there will be three types of classes involves in this process.
Client Class: The client class is a class which depends on the service class.
Service Class: The service class is a class that provides service to the client class.
Injector Class: The injector class injects the service class object into the client class.
The injector class injects dependencies in three ways: through a constructor, or through a property, or through a method. so, there are three types of Dependency Injection which are:
Constructor Injection: In the constructor injection, the injector supplies the dependency through the client class constructor.
Property Injection: In the property injection, the injector supplies the dependency through a public property of the client class.
Method Injection: In the method injection, the client class implements an interface which declares the method(s) to supply the dependency and the injector uses this interface to supply the dependency to the client class.
Let come back to the IoC Container.
IoC Container manages object creation and it's life-time, and also injects dependencies to the class. The IoC container creates an object of the specified class and also injects all the dependency objects through a constructor or a property or a method at run time and disposes it at the appropriate time. This is done so that we don't have to create and manage objects manually.
All the containers must provide easy support for the following DI lifecycle.
Register: The container must know which dependency to instantiate when it encounters a particular type. This process is called registration. Basically, it must include some way to register type-mapping.
Resolve: When using the IoC container, we don't need to create objects manually. The container does it for us. This is called resolution. The container must include some methods to resolve the specified type, the container creates an object of the specified type, injects the required dependencies if any and returns the object.
Dispose: The container must manage the lifetime of the dependent objects. Most IoC containers include different lifetime managers to manage an object's lifecycle and dispose it.
There are many open source containers available for .NET. Some are listed below.
Here BindableBase class i used from the prism if you are not using Prism you will not get BindableBase class you have to implement INotifyPropertyChanged
public class CharInfo : BindableBase
{
private string _charecter;
public string Charecter
{
get { return _charecter; }
set
{
_charecter = value;
SetProperty(ref _charecter, value);
}
}
private int _position;
public int Position
{
get { return _position; }
set
{
_position = value;
SetProperty(ref _position, value);
}
}
}
Add these Property, Command and method to ViewModel
private ObservableCollection<CharInfo> matchedInfo = new ObservableCollection<CharInfo>();
public ObservableCollection<CharInfo> MatchedInfo { get => matchedInfo; set => SetProperty(ref matchedInfo, value); }
private string text;
public string Text { get => text; set => SetProperty(ref text, value); }
private string charToMatch;
public string CharToMatch { get => charToMatch; set => SetProperty(ref charToMatch, value); }
private ICommand matchCharCommand;
public ICommand MatchCharCommand
{
get
{
if (matchCharCommand == null)
{
matchCharCommand = new DelegateCommand(MatchChar);
}
return matchCharCommand;
}
}
private void MatchChar()
{
MatchedInfo.Clear();
if (!string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(CharToMatch))
{
for (int i = 0, pos = -1; i < Text.Length;)
{
pos = Text.IndexOf(CharToMatch, pos + 1);
if (pos < 0)
break;
MatchedInfo.Add(new CharInfo { Charecter = charToMatch, Position = pos });
Step 1: Right click on project
Step 2: Click on Add and select New Item
Step 3: Search for Custom Control
Step 4: Select Custom Control(WPF)
Step 5: Write ExpandableTextBox in Name and click on Add
It will create one class with the Name ExpandableTextBox with following code:
namespace CustomControl.Controls
{
public class ExpandableTextBox : Control
{
static ExpandableTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExpandableTextBox), new FrameworkPropertyMetadata(typeof(ExpandableTextBox)));
}
}
}
It will also create a folder called Themes inside this folder it will create ResourceDictionary with the Name Generic with folowing code:
Generic.xaml
<ResourceDictionary
xmlns:local="clr-namespace:Wpf"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Style TargetType="{x:Type local:ExpandableTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ExpandableTextBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<!--#region newlly added code -->
<Grid x:Name="MainGrid">
<TextBox x:Name="ExpandableTextBox" AcceptsReturn="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"/>
<ResizeGrip x:Name="Grip" Cursor="SizeNWSE" HorizontalAlignment="Right" VerticalAlignment="Bottom" Height="14"
Width="14"/>
</Grid>
<!--#endregion-->
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Now lets modify the ExpandableTextBox class code
[TemplatePart(Name = "ExpandableTextBox", Type = typeof(TextBox))]
[TemplatePart(Name = "Grip", Type = typeof(ResizeGrip))]
[TemplatePart(Name = "MainGrid", Type = typeof(Grid))]
public class ExpandableTextBox : Control
{
static ExpandableTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExpandableTextBox), new FrameworkPropertyMetadata(typeof(ExpandableTextBox)));
}
TextBox _expandableTextbox;
ResizeGrip _resizeGrip;
Grid _mainGrid;
Point LastPoint;
private bool _dragInProgress;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (this.Template != null)
{
_expandableTextbox = this.Template.FindName("ExpandableTextBox", this) as TextBox;
_resizeGrip = this.Template.FindName("Grip", this) as ResizeGrip;
_mainGrid = this.Template.FindName("MainGrid", this) as Grid;
if (_resizeGrip != null)
ExpandTextBox();
}
}
private void ExpandTextBox()
{
_resizeGrip.MouseDown += (s, e) =>
{
LastPoint = Mouse.GetPosition(_resizeGrip);
_dragInProgress = true;
};
Application.Current.MainWindow.MouseUp += (s, e) => _dragInProgress = false;
Application.Current.MainWindow.MouseMove += (s, e) =>
{
if (_dragInProgress)
{
var x = e.GetPosition(_expandableTextbox);
var y = _expandableTextbox.VerticalOffset;
Point point = Mouse.GetPosition(_resizeGrip);
var offset_x = point.X - LastPoint.X;
var offset_y = point.Y - LastPoint.Y;
var new_width = _expandableTextbox.RenderSize.Width;
var new_height = _expandableTextbox.RenderSize.Height;
new_width += offset_x;
new_height += offset_y;
if (new_width > 0 && new_height > 0)
{
_expandableTextbox.Width = new_width;
_expandableTextbox.Height = new_height;
var pointToscreen = _expandableTextbox.PointToScreen(x);
_expandableTextbox.SetValue(Canvas.LeftProperty, pointToscreen.X);
_expandableTextbox.SetValue(Canvas.TopProperty, pointToscreen.Y);
_mainGrid.Width = _expandableTextbox.Width;
_mainGrid.Height = _expandableTextbox.Height;
var translatePoint = _mainGrid.TranslatePoint(x, _mainGrid);
_mainGrid.SetValue(Canvas.LeftProperty, translatePoint.X);
_mainGrid.SetValue(Canvas.TopProperty, translatePoint.Y);
LastPoint = point;
}
}
};
}
}
How to use this control in View?
To use this control first add namespace to your UI like this
xmlns:custom="clr-namespace:CustomControl.Controls"
Now you can show ExpandableTextBox like this
<custom:ExpandableTextBox/>