I trying to create new class object that derivative from TextBox -
If there is chars in the TextBox - the new object will show some button and pressing on this button will be able to remove the chars in this TextBox
how can i make the derivative from control in WPF ?
You could create a new UserControl with a textbox and a button. You bind a string property to the textbox and to the visibility-property of your button. Then you create a converter which converts this string to a visibility. Now you bind the Command-property of your button to a command which sets the string property = string.Empty.
A few hints:
How to use Converters:
<UserControl.Resources>
<local:StringToVisibilityConverter x:Key="STV"></local:StringToVisibilityConverter>
</UserControl.Resources>
<Button Visibility="{Binding Path=MyText, Converter={StaticResource ResourceKey=STV}}" />
How your VM could look like:
public class MainViewModel:ViewModelBase
{
private string _mytext;
public string MyText
{
get
{
return _mytext;
}
set
{
_mytext = value;
OnPropertyChanged("MyText");
}
}
private RelayCommand<object> _clearTextCommand;
public ICommand ClearTextCommand
{
get
{
if (_clearTextCommand == null)
{
_clearTextCommand = new RelayCommand<object>(o => ClearText(), o => CanClearText());
}
return _clearTextCommand;
}
}
private void ClearText()
{
MyText = string.Empty;
}
private bool CanClearText()
{
return true;
}
}
Related
I am new to WPF but have an small understanding of MVVM, so far this is what I have implemented.
UpdateTableView - View (Short snippet of larger user control)
<UserContol.DataContext>
<local:UpdateTableViewModel />
</UserContol.DataContext>
<StackPanel>
<TextBox Text="{Binding InputPath}"/>
<TextBlock Content="Placeholder" />
</StackPanel>
UpdateTableModel - Model
public class UpdateTableModel : ObservableObject
{
private string _inputPath;
public string InputPath
{
get
{
return _inputPath;
}
set
{
if (value != _inputPath)
{
_inputPath = value;
OnPropertyChanged("InputPath");
}
}
}
}
ObservableObject
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanaged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanaged;
if (handler != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
And an empty UpdateTableViewModel
class UpdateTableViewModel : ObservableObject { }
My question is how would I use data binding so that as a user when I enter a inputPath in the text box, firstly whatever I type is store in the property _inputPath so I can use it in code behind and additionally be reflected in the text block.
I have done some research and found about one way and two way data binding and can't really work out what else I need to add for my desired functionality.
Thanks in advance.
Your view models must contain the properties you want to bind to.
Generally the TextBox.Text property automatically binds TwoWay. This is the default behavior. So, without specifying the Binding.Mode explicitly, the text entered into the TextBox will be automatically sent to the binding source. In your case the input would be automatically sent to the InputPath property.
UpdateTableModel.cs
public class UpdateTableModel
{
public void SaveUserNameToFile(string filePath, string userName)
{
File.AppendAllText(filePath, userName, Encoding.UTF8);
}
}
UpdateTableViewModel.cs
An implementation of RelayCommand can be found at
Microsoft Docs: Patterns - WPF Apps With The Model-View-ViewModel Design Pattern - Relaying Command Logic
class UpdateTableViewModel : ObservableObject
{
private UpdateTableModel UpdateTableModel { get; }
public ICommand SaveUserCommand => new RelayCommand(SaveUserName);
private string _userName;
public string UserName
{
get => _userName;
set
{
if (value != _userName)
{
_userName = value;
OnPropertyChanged(nameof(UserName));
}
}
}
private string _inputPath;
public string InputPath
{
get => _inputPath;
set
{
if (value != _inputPath)
{
_inputPath = value;
OnPropertyChanged(nameof(InputPath));
}
}
}
public UpdateTableViewModel()
{
this.UpdateTableModel = new UpdateTableModel();
}
// Alternative constructor
public UpdateTableViewModel(UpdateTableModel updateTableModel)
{
this.UpdateTableModel = updateTableModel;
}
private void SaveUserName(object param)
{
// Pass the data to the model
this.UpdateTableModel.SaveUserNameToFile(this.InputPath, this.UserName);
}
}
UpdateTableView.xaml
<UserControl>
<UserContol.DataContext>
<local:UpdateTableViewModel />
</UserContol.DataContext>
<StackPanel>
<TextBox Text="{Binding UserName}" />
<TextBox Text="{Binding InputPath}" />
<Button Command="{Binding SaveUserCommand}"
Content="Save to File" />
</StackPanel>
</UserControl>
i wrote a simple custom control that inherits from textbox and uses a custom inputbox to enter text:
class TextTextbox : TextBox
{
public string InputBoxTitle { get; set; }
public string Input { get; set; }
public TextTextbox()
{
PreviewMouseDown += MyTextbox_MouseDown;
}
private void MyTextbox_MouseDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
TextBox tb = (TextBox)sender;
var dialog = new BFH.InputBox.InputBox(InputBoxTitle, Input);
dialog.ShowDialog();
if (!dialog.Canceled)
tb.Text = dialog.Input;
else
tb.Text = Input;
}
}
i use it in the view like this:
<CustomControls:TextTextbox Text="{Binding Test}" InputBoxTitle="Titel" Input="Input"/>
in the vm for tests:
private string _test;
public string Test
{
get { return _test; }
set
{
if (_test == value)
return;
_test = value;
RaisePropertyChanged("Test");
}
}
but the binding to Test is not working. in the view, i see that the text of the textbox does change, but i seems not to be linked to the property of the VM. i added a button with MessageBox.Show(Test), but it is always empty. what am i doing wrong here?
You need to set the binding's UpdateSourceTrigger property to PropertyChanged. Otherwise the source property Test will not be updated before the TextBox loses focus.
<CustomControls:TextTextbox
Text="{Binding Test, UpdateSourceTrigger=PropertyChanged}" ... />
From the Examples section on the Binding.UpdateSourceTrigger page on MSDN:
The TextBox.Text property has a default UpdateSourceTrigger value of
LostFocus. This means if an application has a TextBox with a
data-bound TextBox.Text property, the text you type into the TextBox
does not update the source until the TextBox loses focus (for
instance, when you click away from the TextBox).
My ViewModel
public class MyViewModel : ViewModelBase
{
// INotifyPropertyChanged is implemented in ViewModelBase
private String _propX;
public String PropX
{
get { return _propX; }
set
{
if (_propX != value)
{
_propX = value;
RaisePropertyChanged(() => PropX);
}
}
}
private String _propY;
public String ServerIP
{
get { return _propY; }
set
{
if (_propY != value)
{
_propY = value;
RaisePropertyChanged(() => ServerIP);
}
}
}
public A()
{
this._propY = "000.000.000.000";
this._propY = "000.000.000.000";
}
}
// EDIT
// This is the command that resets the properties
private RelayCommand _resetFormCommand;
public ICommand ResetConnectionFormCommand
{
get
{
if (_resetFormCommand == null)
{
_resetFormCommand = new RelayCommand(param => this.ExecuteResetFormCommand(), param => this.CanExecuteResetFormCommand);
}
return _resetFormCommand;
}
}
private bool CanExecuteResetFormCommand
{
get
{
return !String.IsNullOrWhiteSpace(this._propX) ||
!String.IsNullOrWhiteSpace(this._propY);
}
}
private void ExecuteResetFormCommand()
{
this._propX = "";
this._propY = "";
}
My View xaml
<TextBox Name="propX" Text="{Binding PropX }" PreviewTextInput="textBox_PreviewTextInput" />
<TextBox Name="propY" Text="{Binding PropY }" PreviewTextInput="textBox_PreviewTextInput" />
<Border>
<Button Content="Reset" Name="resetBtn" Command="{Binding ResetFormCommand}" />
</Border>
My View code behind
private MyViewModel vm;
public ConnectionUserControl()
{
InitializeComponent();
vm = new MyViewModel();
this.DataContext = vm;
}
private void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
ValidateInput(sender as TextBox, e);
}
The reset command resets the properties in my view model but the textboxes still contain their values, the binding is not working properly :(
Am i missing something here?
You should reset the properties, not the private members:
private void ExecuteResetFormCommand()
{
this.PropX = "";
this.PropY = "";
}
How are you resetting the values? You may be overriding the databinding when you reset the values. It would be helpful if you post the code that gets executed when the button is clicked.
In your xaml-code you have to set the binding like:
<TextBox Name="propX" Text="{Binding PropX, Mode=TwoWay}" .../>
binding has to be two way in order for textbox to update itself from viewmodel
In your Code-behind, you have a property ServerIP, which I think you wanted to be named as PropY, since your TextBox binds to a PropY property.
<TextBox Name="propY" Text="{Binding PropY }" PreviewTextInput="textBox_PreviewTextInput" /
Also, you should be assigning the value to your property in your ExecuteResetFormCommand command, and not your private member since the private member does not trigger INotifyPropertyChanged
private void ExecuteResetFormCommand()
{
this.PropX = "";
this.PropY = ""; // <-- Currently you have PropY declared as ServerIP
}
I have a Xaml Page With A textbox And a button When the User Clicks the Button The text box value should be passed to its viewModel. How to achieve this?
xaml:
<TextBox Text={Binding TextBoxAContent} />
<Button Command={Binding ButtonCommand} />
view model code should be something like this:
class MainPageViewModel : ViewModelBase
{
private string _textBoxAContent;
public string TextBoxAContent
{
get {return _textBoxAContent;}
set {
_textBoxAContent = value;
RaisePropertyChanged("TextBoxAContent");
}
}
public ICommand ButtonCommand
{
get
{
return new RelayCommand(ProcessTextHandler);
}
}
private void ProcessTextHandler()
{
//add your code here. You can process your textbox`s text using TextBoxAContent property.
}
}
You should also assign your viewmodel to view via DataContext property of the view control. (simply in constructor)
public MainPage()
{
DataContext = new MainPageViewModel();
}
UPD
p.s. RelayCommand & ViewModelBase - classes from MVVM Light
I have a static collection of items (say numbers from 1 to 100) that I'm presenting in a ComboBox. I can bind these items to the ComboBox no problem.
My question is how to bind a second ComboBox to a subset of those items. The behavior I want is to have the second ComboBox bound to the subset of items remaining after the first ComboBox is selected. For example the first ComboBox would show 1,2,3...,100. If the number 43 is selected in the first ComboBox then the second ComboBox should show 44,45,...,100.
How can this be accomplished and have the second ComboBox update if the first is changed without a lot of code-behind?
I would do this with a using MVVM pattern.
Create a class that implements INotifyChange and expose three Property.
ICollection FullCollection
int FirstIndex
ICollection PartialCollection
Use this class as DataContext for your Control and bind SelectedIndex of the first combo box to FirstIndex property, ItemSource of first combobox to FullCollection and ItemSource of second collection to PartialCollection (Be sure that SelectedIndex binding mode is Two Way).
Then on set of FirstIndex property set the PartialCollection property as you want.
Remember that you have to use NotifyPropertyChange on set method of each Properties.
Hope this help.
I'd go with MVVM design but if you want to just do testing and want to see quick results without going into design patterns then I'd suggest using something like CLINQ / BLINQ / Obtics framework to leverege the power of LINQ while keeping the results live for the combo box.
Since LoSciamano has already posted a reply on that (while i was posting this!) I won't dive into details of MVVM implementation
I would expose the first collection as an ObservableCollection<T> and the second collection as a bare IEnumerable. You can then use the default collection view to handle the events necessary to re-filter the sub collection.
class FilteredVM : ViewModelBase
{
public ObservableCollection<MyVM> Items { get; private set; }
public IEnumerable<MyVM> SubItems { get; private set; }
public FilteredVM()
{
this.Items = new ObservableCollection<MyVM>();
this.SubItems = Enumerable.Empty<MyVM>();
var view = CollectionViewSource.GetDefaultView(this.Items);
view.CurrentChanged += (sender, e) => { SetupFilter(); };
view.CollectionChanged += (sender, e) => { SetupFilter(); };
}
private void SetupFilter()
{
var view = CollectionViewSource.GetDefaultView(this.Items);
var current = view.CurrentItem;
if (current != null)
{
this.SubItems = this.Items.Where((vm,idx) => idx > view.CurrentPosition);
}
else
{
this.SubItems = Enumerable.Empty<MyVM>();
}
this.OnPropertyChanged("SubItems");
}
}
Alternatively, if you'd like to keep CollectionViewSource out of your VM:
class FilteredVM : ViewModelBase
{
private MyVM selectedItem;
public MyVM SelectedItem
{
get { return this.selectedItem; }
set
{
if (value != this.selectedItem)
{
this.selectedItem = value;
this.OnPropertyChanged("SelectedItem");
this.SetupFilter();
}
}
}
public ObservableCollection<MyVM> Items { get; private set; }
public IEnumerable<MyVM> SubItems { get; private set; }
public FilteredVM()
{
this.Items = new ObservableCollection<MyVM>();
this.SubItems = Enumerable.Empty<MyVM>();
this.Items.CollectionChanged += (sender, e) => { this.SetupFilter(); };
}
private void SetupFilter()
{
if (this.SelectedItem != null)
{
var item = this.SelectedItem; // save for closure
this.SubItems = this.Items.SkipWhile(vm => vm != item).Skip(1);
}
else
{
this.SubItems = Enumerable.Empty<MyVM>();
}
this.OnPropertyChanged("SubItems");
}
}
Keep in mind this will require SelectedItem to be properly bound in the View to the ViewModel. The first approach listed allows the SelectedItem to be bound to anywhere (or nowhere).
Have 2 Observable collections so that when item in the 1st box is selected, it will kick off method which clears and re populates the 2nd collection. As it is observableCollection, it gets reflected in the WPF GUI automatically
Here is a concrete example (as usual, may be improved, but the idea is here) :
Code behind an view model :
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
Initialsource = new ObservableCollection<int>();
for (int i = 0; i < 101; i++)
{
Initialsource.Add(i);
}
}
private int _selectedsourceItem;
public int SelectedsourceItem
{
get { return _selectedsourceItem; }
set
{
_selectedsourceItem = value;
SubsetSource = new ObservableCollection<int>(Initialsource.Where(p => p > _selectedsourceItem));
InvokePropertyChanged(new PropertyChangedEventArgs("SubsetSource"));
InvokePropertyChanged(new PropertyChangedEventArgs("SelectedsourceItem"));
}
}
private ObservableCollection<int> _initialsource;
public ObservableCollection<int> Initialsource
{
get { return _initialsource; }
set
{
_initialsource = value;
InvokePropertyChanged(new PropertyChangedEventArgs("Initialsource"));
}
}
private ObservableCollection<int> _subsetSource;
public ObservableCollection<int> SubsetSource
{
get { return _subsetSource ?? (_subsetSource = new ObservableCollection<int>()); }
set
{
_subsetSource = value;
InvokePropertyChanged(new PropertyChangedEventArgs("SubsetSource"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
}
XAML :
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal">
<ComboBox Width="100" ItemsSource="{Binding Initialsource}" SelectedItem="{Binding SelectedsourceItem, Mode=TwoWay}"></ComboBox>
<ComboBox Width="100" ItemsSource="{Binding SubsetSource}"></ComboBox>
</StackPanel>
</Grid>
You can use my ObservableComputations library:
ObservableCollection<Item> itemsSubset = ItemsObservableCollection
.Skiping(fromIndex)
.Taking(itemsInSubsetCount);
itemsSubset reflacts all the changes in ItemsObservableCollection.