In my 'View' I have a TextBox bound to a ViewModel's string property.
I want to add a submit button to the View, so the underlying ViewModels string property is only updated when this is pressed.
To further complicate things, this TextBox is inside a DataGrid. I think setting the bindings UpdateSourceTrigger to Explicit may be the answer but I can't see how this would work.
Any alternative solution would be to switch the ViewModels String with a TextBox - meaning I would manually populate data.
You can bind button to command and pass text of textbox as parameter.
<TextBox x:Name="textBox"></TextBox>
<Button Content="Button" Command="{Binding MyCommand}" CommandParameter="{Binding ElementName=textBox, Path=Text}"/>
In your ViewModel:
public ICommand MyCommand
{
get
{
return new RelayCommand((textBoxText) =>
{
if (...)
{
//somelogic;
}
});
}
}
In the Button you access the row via the DataContext
private void ButtonRevise_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
GabeLib.SearchItem srchItem = (GabeLib.SearchItem)btn.DataContext;
Related
I have a user control with two buttons (Add, Delete). When accessed from the main window, how do I know which button were being clicked?
btnAdd had a method while btnDelete has another method that should be invoked.
Create two separate Command for button add and delete in MainWindow
And you can bind your command in userControl like this:
<Button Command={Binding AddCommand}/>
and set the datacontext of Mainwindow to itself inside XAML or in a constructer
this.datacontext = this;
For more information on how to create Command see this and this
If I understand your issue correctly, you could cast the sender argument in the event handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
if (button.Name == "btnAdd ")
{
//add button was clicked...
}
else if (button.Name == "btnDelete")
{
}
}
<Button x:Name="btnAdd" Content="Add" Click="Button_Click" />
<Button x:Name="btnDelete" Content="Delete" Click="Button_Click" />
i'm new to WPF. I have an application with multiple tabs. In one tab I can insert data into a table in the database. In another tab I have a combobox with itemsource of the table mentioned earlier. I want to update the combobox items when user want to chose from the combobox./
I tried with the GotFocus property in the following way:
private void ComboBoxOperatingPoints_GotFocus_1(object sender, RoutedEventArgs e)
{
this.ThisViewModel.UpdateModel();
}
Updatemodel function contains the following:
this.OperatingPoints = new ObservableCollection<Operating_Point>(new OperatingPointRepository().GetAll());
this.NotifyPropertyChanged("OperatingPoints");
The combobox bindind in the XAML :
<ComboBox SelectionChanged="ComboBoxOperatingPoints_SelectionChanged"
x:Name="ComboBoxOperatingPoints"
GotFocus="ComboBoxOperatingPoints_GotFocus_1"
FontSize="30"
HorizontalAlignment="Right"
Margin="40,40,0,0"
VerticalAlignment="Top"
Width="200"
Height="50"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding OperatingPoints}"
DisplayMemberPath="name"
SelectedValue="{Binding OperatingPointID,UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="operating_point_id"
>
The combobox refresh, but gives validation error and I can't use it anymore after the first GotFocus event occured.
Thanks in advance!
EDIT:
Finaly I changed the GotFocus event to DropDownOpened event and it's working fine.
Your code is creating a new ObservableCollection on every update. You probably only want to create the ObservableCollection once, then replace it's contents in UpdateModel. So, for example, in your view model's constructor, instantiate the OperatingPoints collection:
public class MyViewModel {
public MyViweModel() {
this.OperatingPoints = new ObservableCollection<Operating_Point>();
}
}
Then, in UpdateModel:
public void UpdateModel() {
this.OperatingPoints.Clear();
foreach ( Operating_Point point in new OperatingPointRepository().GetAll() ) {
this.OperatingPoints.Add(point);
}
NotifyPropertyChanged( "OperatingPoints" );
}
I have an editor (textbox) and I'd like to bind different texts to it based on which button is clicked.
I could use a command on the button and pass the string which I'd like to edit through commandparameter and update the string which is bound to the textbox. This will work but it's not going to save the modifications because there is no binding between the text (which was passed through the commandparameter) and the textbox's text.
My question is, how should I implement this bind neatly without accessing the textbox directly from the View Model?
edit: it's probably vague what I'm trying to achieve. I try to clarify it a bit:
So let's say I have several different buttons, if I click one of those, it should bind some string to the editor's textbox, where I can modify it and save it later.
<Button Content="Edit query" Command="{Binding ShowQueryInEditorCommand}" CommandParameter="{Binding SomeSqlStringToBeEdited}"/>
<Button Content="Edit query" Command="{Binding ShowQueryInEditorCommand}" CommandParameter="{Binding SomeOtherSqlStringToBeEdited}"/>
This is what the command will execute:
public void ShowQueryInEditor(object o)
{
string SqlStatementParam = o as string;
if (SqlStatementParam != null)
SQLStatement = SqlStatementParam;
}
And the editor TextBox itself:
<TextBox Text="{Binding SQLStatement}">
As you can see, this is very rudimentary as it just sets the SQLStatement string, but there is no bind between them so it cannot reflect the modifications back to SomeSqlStringToBeEdited/SomeOtherSqlStringToBeEdited. This is what I would like to achieve, to bind that string somehow to the textbox when the button is clicked.
There are two basic ways I can think of: through code, or through Xaml.
In code, instead of accessing the textbox from the ViewModel, add a new property to the ViewModel for the "DisplayText" or "SelectedText", or whatever makes sense in your scenario. Bind your textbox to that property instead, and then put the rest of the logic you need inside the setter (or, if it's a DependencyProperty, the OnPropertyChanged callback). That keeps all the logic in your ViewModel and means the Xaml doesn't have to care.
Or in Xaml, you could use triggers and templates to change the textbox depending on the selected button. Most likely form your description, I would suggest having multiple textboxes, one bound to each string, and switch the visible textbox based on the button that's clicked. This keeps your ViewModel ignorant of this display-specific logic, and allows you to change it more easily later on.
Personally, I would likely suggest the Xaml approach, but it will depend on your specific situation.
According to
but the problem is that the buttons are created dynamically
1) Wrap you query text and button into view model like this:
public class ViewModel : ViewModelBase
{
public ViewModel()
{
this.turnIsSelectedOnCommand = new RelayCommand(() => IsSelected = true);
}
public String Text
{
get { return text; }
set
{
if (text != value)
{
text = value;
OnPropertyChanged("Text");
}
}
}
private String text;
public Boolean IsSelected
{
get { return isSelected; }
set
{
if (isSelected != value)
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
private Boolean isSelected;
public RelayCommand TurnIsSelectedOnCommand
{
get { return turnIsSelectedOnCommand; }
}
private readonly RelayCommand turnIsSelectedOnCommand;
}
2) Put your dynamically created text/buttons into collection. For simplicity, I've added them to array:
public MainWindow()
{
InitializeComponent();
DataContext = new[]
{
new ViewModel { Text = "SELECT * FROM Foo", IsSelected = true },
new ViewModel { Text = "SELECT * FROM Bar" },
new ViewModel { Text = "DROP TABLE Foo" },
new ViewModel { Text = "DROP TABLE Bar" },
};
}
3) Bind the collection with ListBox, and the editor - with the Text of the selected item:
<ListBox Grid.Row="0" Margin="5" ItemsSource="{Binding}" x:Name="lbItems">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Right"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="Edit query" Command="{Binding TurnIsSelectedOnCommand}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Grid.Row="1" Margin="5" Text="{Binding Path=SelectedItem.Text, ElementName=lbItems}" />
I've added some style modifications.
First modifies buttons layout.
Second means, that when you will push the button and ViewModel will become selected, list box item will be selected too.
Third hides selection from the background of the selected item.
Hope, this helps.
Control different DataContext in WPF
As I can deploy multiple DataContext in different tabs and control which is the current DataContext
I'm using Mvvm Light WPF4 i have the different ViewModels, View but i dont know how to handle multiples DataContext and control the current DataContext for change on tab switch
Edit:
I've got an approach to the solution as follows:
Create a ViewModel for the MainView
The tabcontrol source is a ObservableCollection
Each TabItem has its own DataContext
The menu has the DataContext like this: DataContext="{Binding Path=CurrentTab.DataContext}" where CurrentTab change when add new TabItem in the ViewModel
i have the following problems:
how do I connect the ViewModel from the TabControl when you change the tab?
Solution: the problem is that Mvvm Light uses a ViewModelLocator for Binding ViewModel in static way, this is the problem when i add tab in C# the ViewModelLocator dont works, in other way i need load manually the ViewModel for each tab like this:
// in MainModelView.cs
public RelayCommand MyCommand { get; set; }
private void RegisterCommand()
{
MyCommand = new RelayCommand(() =>
{
AddTab("Tab Header", new TabViewModel(), new TabContentControl());
});
}
private void AddTab(string header, object context, ContentControl content)
{
TabItem = null;
foreach(TabItem tab in TabItemList)
{
if(tab.Header.Equals(header);
{
tabItem = tab;
}
}
if(null == tabItem)
{
tabItem = new TabItem();
tabItem.Header = header;
tabItem.Content = content;
tabItem.DataContext = context;
TabItemList.Add(tabItem);
}
CurrentTabIndex = TabItemList.IndexOf(tabItem);
}
2.the DataContext dont update in the menu, my code is wrong?
Solution: the previous point solve this too and only with the follow code solved:
// in RegisterCommands()
ChangeTabCommand = new RelayCommand<TabItem>(tab =>
{
if (null == tab) return;
CurrentTabContext = tab.DataContext;
}
in MainWindow.xml:
<!-- MainWindow.xaml -->
<Button Content="NewTab" Command="{Binding Path=MyCommand }" />
<TabControl
Margin="5 5 5 0"
Grid.Row="1"
ItemsSource="{Binding Path=TabItemList}"
SelectedIndex="{Binding Path=CurrentTabItemIndex}"
x:Name="Workspace">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand
Command="{Binding ChangeTabCommand }"
CommandParameter="{Binding SelectedItem, ElementName=Workspace}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TabControl>
Edit 2:
How to avoid modifying the view in the ViewModel and send the necessary parameters from the same view (ContenControl, Header, Context)
I would create a ViewModelContainer that has a property for each of your view models (e.g. MainViewModel, Tab1ViewModel, Tab2ViewModel).
The you can bind ViewModelContainer as DataContext of the Window and bind each TabItem DataContext to the right VM object in this way DataContext="{Binding Tab1ViewModel}"
No suggestion for problem 2.
Update
Your code does not follow MVVM at 100%. Your command edit the view and viewmodel. If you want to be painstaking the command must interact only with viewmodel. The viewmodel then will signal (through an ObservableCollection or an INotifyPropertyChanged interface) the view that will reply by adding a new tabItem.
I suppose that the View part can be managed 100% with XAML using ItemTemplate to define how the panels should appear.
Some of the checkboxes on my form should not be able to be checked/unchecked by users. Is there a way for me to cancel the event before the checbox's Check event is triggered?
in winForms it was easy, just
public void cb_BeforeChecked(object sender, EventArgs e){
e.Handled = true;
}
but I cannot find anything like this in WPF...I figure you can probably do it, just need to do something fancy..
Thanks!
Why not just set IsEnabled="False"?
You can set IsHitTestVisible="False" to make it not respond to user clicks. Otherwise you can bind it to a command if viewmodel logic determines whether it is clickable.
<Grid>
<CheckBox IsHitTestVisible="False" Content="I cannot be clicked at all"/>
<CheckBox Command="{Binding DoSomethingCommand}" Content="I can be clicked if DoSomethingCanExecute returns true."/>
</Grid>
In your DataContext (Viewmodel or otherwise):
RelayCommand _DoSomethingCommand = null;
public ICommand DoSomethingCommand
{
get
{
if (_DoSomethingCommand== null)
{
_DoSomethingCommand= new RelayCommand(
param => DoSomething(),
param => DoSomethingCanExecute
);
}
return _DoSomethingCommand;
}
}
public bool DoSomethingCanExecute
{
get
{
return CheckboxShouldBeEnabled();
}
}
public void DoSomething()
{
//Checkbox has been clicked
}
This might be a bit of an overkill, but you could sub-class CheckBox and then override the OnClick() method.
Only setting IsHitTestVisible="False" just takes care of the Mouse, the users can still use the KeyBoard to tab to the CheckBox and change the value.
You should set both IsHitTestVisible="False" and Focusable="False" to disable the KeyBoard as well
You can have the check box disabled, and associate a style with the disabled check box, if the disabled look is a problem. As its already pointed in the previous posts, its good to have different looks for different states.