I am new to MVVM, I have a checkedlistbox in a view with the list of titles(have bound the exposed property in ViewModel to this checkedlistbox control)...
Here is my XAML code that populates the ListCheckBox -
<ListBox x:Name="lstCode" ItemsSource="{Binding Code,Mode=TwoWay}" Grid.Row="1" Style="{StaticResource ListBoxStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox x:Name="chkBox" IsChecked="{Binding IsChecked,Mode=TwoWay}" Content="{Binding Code_Name}" Margin="0" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This control shows the correct list of items with checkboxes for each item in the listbox...
What should be the code in viewmodel to make it work in two way - while getting the codes from database, it should automatically selected the code from the listcheckedbox and when the user selects one or more codes, the viewmodel should be able to know the items selected...
In general, for TwoWay binding, you will need to implement the INotifyPropertyChanged interface on the ViewModel you want to bind to.
In this case, your ViewModel will have to provide a property that returns a collection that your view can bind to, e.g. an ObservableCollection.
This ObservableCollection already allows you to add, update, and delete items in that list in a way that automatically communicates the changes between View and ViewModel.
For the rest I suggest to start digging into MVVM depths. To fully take advantage of WPF's capabilities, you will need to understand the basics for yourself. A great starting point is this SO thread: MVVM: Tutorial from start to finish?
Related
I have a datagrid of rows which contain data read from a web server and values I want to write into a webserver. I write the values in getting the user to input a number into the appropriate column and click an adjacent text box;
<DataGrid x:Name="datagridDERControl" HorizontalAlignment="Center" VerticalAlignment="Center" Background="#FF322D2D" Height="382" Margin="10,78,10,10" Width="972" ItemsSource="{Binding Path=NFDataSource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="100" Header="Write Set Point">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="{Binding Path=WriteSetPoint, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="Global Trip">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Name="buttonGlobalTrip" Width="100" Click="buttonGlobalTrip_Click"></Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid
How do I extract the specific textbox string per row to use in my view model.
It's always difficult to answer a question where the relevant details have been omitted by the question author. However, I shall try!
You have data bound (presumably) a collection property named NFDataSource to your DataGrid.ItemsSource property. That is the collection that represents the data in your DataGrid, so to 'extract' a specific value, you need to look into your data items in your collection.
One handy property in the DataGrid class is the SelectedItem property. this enables you to data bind an object (of the same type as those in your NFDataSource collection) to this property, which accesses the data object behind the row that is currently selected in the UI:
<DataGrid ItemsSource="{Binding NFDataSource}" SelectedItem="{Binding SelectedItem}" />
Now you can utilise your SelectedItem property to access the values from the selected row in the DataGrid:
string someValue = SelectedItem.SomeProperty;
As you tagged this with MVVM and databinding, I'll assume you're using these and have just got muddled.
"I have a datagrid of rows which contain data read from a web server
and values I want to write into a webserver."
So your viewmodel has a property, which is a collection of a custom class, that represents data fetched from a webservers.
"I write the values in getting the user to input a number into the
appropriate column and click an adjacent text box"
So this VM property is two-way bound to a datagrid, so that each item in the collection represents 'one row', and the properties on those items represent your 'columns'. The user can make changes to the UI displayed values, and because of the two way databinding the VM property is also updated.
"How do I extract the specific textbox string per row to use in my
view model."
Why do you need the specific textbox string, if it is databound to a property (or rather to a property on a class contained in a collection) in your VM anyway? If you've set up your VM this way, and are using databinding, you rarely need to worry about UI specific things such as which row in a datagrid is clicked.
As Sheridan points out though, you can also bind to properties on the datagrid such as SelectedItem so that you can perform additional operations beyond just reading/writing data. SelectedItem for your datagrid will be of the type that populates your VM collection, so will have the appropriate properties.
For example, if your VM collection is an IQueryable<Person> and that is bound to the ItemsSource of the datagrid then the SelectedItem will be of type Person. You could then have a VM property called SelectedPerson which is bound to that SelectedItem, and access things like SelectedPerson.Name etc.
In a WPF application I have TreeView and ListBox controls, both of which have their SelectedItem dependency property bound to the same property in the view model. So when I have selected items in both controls and try to access that view model's property it behaves unpredictably. Is it possible to remove selection on one control when item is selected in another one?
Sample code (CurrentNamedObject is a view model property and the same for both controls):
<controls:XTreeView Grid.Column="0" Grid.Row="1">
<i:Interaction.Behaviors>
<wpf:BindableSelectedItemBehavior SelectedItem="{Binding ViewModel.CurrentNamedObject, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</i:Interaction.Behaviors>
...
<ListBox Grid.Column="2" Grid.Row="1" SelectedItem="{Binding ViewModel.CurrentNamedObject}">
...
Update:
The basic idea behind this is following:
There are two types of objects which implement the same interface (IModifiable). One type of object has descendant objects so I chose to display it in a treeview; another one is a plain object, so I display it in a listbox. Now, I have a toolbox with modification commands that are applicable to both types of object, so I bound both objects to the same property. In essence, I want, for example, to call Rename method on the selected item regardless of whether it's a treeview or a listbox item.
I need to bind the listbox with list of items retreived from database. Each list item is displayed as checkbox in listbox.
<ListBox.ItemTemplate>
<HierarchicalDataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
Along with the checkbox, I also need to add a checkbox as a list item.
Could anyone let me know as to how this can be done?
DataTemplateSelector options within WPF this allow a very small amount of backend code to take care of all the selections for you. This allows you to make use of this code from within an MVVM structure without any issues, and while making the front end extremely easy to manage without complicating the back end code.
here is a sample.
Use DataTemplateSelector if you want to use two different visuals as list items.
I have a TreeView that I am binding to an ItemsSource that creates a CheckBox for each item. Here is the xaml:
<TreeView x:Name="ReasonTreeView" Height="Auto" Background="Transparent"
BorderThickness="0" IsTabStop="False"
ItemsSource="{Binding Path=AnswerOptions}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type QSB:Answer}" ItemsSource="{Binding Path=AnswerOptions}">
<StackPanel Orientation="Horizontal">
<CheckBox Margin="0,5"
IsChecked="{Binding Path=IsSelected}"
IsEnabled="{Binding Path=Value,
Converter={StaticResource ReasonValueToEnabledConverter}}"
Visibility="{Binding Path=AnswerOptions,
Converter={StaticResource ParentNodeVisConverter}}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
In my application I then create multiple instances of these. Depending on the instance of the TreeView, certain CheckBoxes need to be disabled so the user can not select them, however I'm uncertain of how I can access the individual items in the HierarchicalDataTemplate in the code.
After looking around for a while the only thing I can think of is to build the whole TreeView in the code behind instead of the xaml, but I would rather not have to resort to that. Is there anything else that I can do?
To help clarify my point and for illustrative purposes, this is essentially what I want to be able to do (in pseudocode): ReasonTreeView.ItemsSource[5].IsEnabled = false;
Which would disable the CheckBox (and any other controls in that HierarchicalDataTemplateItem) at index 5 of the TreeView's ItemsSource
Let me know if more information is needed
I meant that binding on the checkbox's isenabled property Path=Value. That Value member has to be bool and implement INotifyPropertyChanged then you can control IsEnabled from your model. Dont forget to add Mode=Twoway to your binding
Instead of accessing the CheckBox through Control.ItemsSource property you should make the change in your underlying collection (that is itemssource of your control). After making the change notify the View (your Control) that data has been changed so update the control.
Implement INotifyPropertyChanged in your underlying class and after changing the Property (which is responsible for Enabled/Disabled) value Notify the View.
If you are not familiar with concepts of Data Binding and INotifyPropertyChanged, I would suggest you to read some basic tutorials about it. It is one of the major feature of WPF which makes life very easy for doing things like yours
I'm new to WPF and using MVVM. I have a view in which I want to display different content according to what a user selects on a menu. One of those things is another user control Temp which has a view model (TempVM) so I am doing this:
<ContentControl Content="{Binding Path=TempVM}"/>
and TempVM (of type TempViewModel)is null until the user clicks a button. Its data template is this
<DataTemplate DataType="{x:Type vm:TempViewModel}">
<view:Temp />
</DataTemplate>
That's fine, but the other thing I want to do is show a listbox when a user clicks a different menu item. So I am trying to do
<ContentControl Content="{Binding Path=Missions}"/>
(Missions is an observable collection of MissionData) and trying to template it like this:
<DataTemplate DataType="{x:Type ObservableCollection(MissionData)}">
<StackPanel>
<ListBox ItemsSource="{Binding}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go"/>
</StackPanel>
</DataTemplate>
But the compiler doesn't like the type reference. If I try doing it by giving the template a key and specifying that key in the ContentControl it works but obviously I see the ListBox and button when there's no Missions. Obviously I could make a user control and viewmodel and follow the same pattern as I did for the TempVM but it seems over the top. Am I going the right way about this and what do I need to do?
From what i see is that you try to use a Collection as a dataobject which is in my opinion bad practice. Having a DataTemplate for a collection is also problematic, like you already have witnessed. I would advice you to use a ViewModel for your missions collection.
class MissionsSelectionViewModel
{
public ObservableCollection<Mission> Misssions;
public MissionData SelectedMission;
public ICommand MissionSelected;
}
and modify your datatemplate to
<DataTemplate DataType="{x:Type MissionsSelectionViewModel}">
<StackPanel>
<ListBox ItemsSource="{Binding Missions}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go" Command="{Binding MissionSelected}/>
</StackPanel>
</DataTemplate>
If I were to follow your pattern of implicit templates, I would derive a custom non-generic collection MissionDataCollection from ObservableCollection<MissionData> and use it to keep MissionData items. Then I would simply reference that collection in DataType. This solution gives other advantages like events aggregation over the collection that are useful.
However, it seems to me that the best solution is the following.
Add a IsMissionsListVisible property to your VM.
Bind the Visibility property of the ContentControl showing the list to the IsMissionsListVisible property.
Use a keyed DataTemplate resource.
Implement the logic that determines if IsMissionsListVisible. Supposedly it should be true when there is at least one mission in the selected item. But the logic may be more complex.
I would do it this way. In fact, I do it this way usually, and it gives several benefits. The most important is that I can explicitly control the logic of content visibility in various situations (e.g. async content refresh).