|
Hi..
Thanks for the reply.
But I have situation for getting search criteria from the page and perform the operation in the button click event based on passing the search criteria to the view model.
So, How can I get view's control value in the viewmodel?
Can u pls give a hint about how to overcome such type of situations?
Thanks..
|
|
|
|
|
The search criteria should be bound to a property on the VM. You then have the data in the VM, responding to the event (click the search or the key up/down) need to be handled.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Set the view's datacontext to the view model.
You could then apply commands to handle the button command click in the view model.
The funniest thing about this particular signature is that by the time you realise it doesn't say anything it's too late to stop reading it.
|
|
|
|
|
Hello to All,
Can anyone can help me get data from the ViewModel(DataContext of the page) in Page's Code behind using MVVM architecture.
Thanks in Adv..
|
|
|
|
|
this.LayoutRoot.DataContext
This assumes the layoutroot has the data context assigned (otherwise just this.datacontext). You will also need to cast the datacontext back to your viewmodel
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
You should be setting the Datacontext of the page to the viewmodel somewhere if you are implementing MVVM correctly.
The funniest thing about this particular signature is that by the time you realise it doesn't say anything it's too late to stop reading it.
|
|
|
|
|
One of the aspects of MVVM is that you don't have any code behind. The only thing thats "allowed" in the code behind is manipulating visuals. The fact that you want to interact with the ViewModel from the code behind is definitely wrong.
|
|
|
|
|
Hi,
can any one tell me how should i change the display of a combobox as a hyperlink, and when clicked on hyperlink it has to replace it by the combobox in the same place and display combobox item in the dropdown manner attached to the hyperlink...
|
|
|
|
|
This really sounds ugly, bleh
Use a datatemplate to create the comboboxitem with the HL
Create a second combo with standard formatting
You then have a number of events that need to be managed.
Click event on the HL
Do what you need with the data and set a property that makes the original CB collapsed and a second CB visible.
How you recover back to the original HL combo is a mystery you will need to unravel.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Yeah man, like Mycroft said... randomly changing a hyperlink <-> combobox is UGLY and not a very good UI IMO. Controls shouldn't randomly change types. It might make sense in your head, but it wouldn't make sense to a user.
|
|
|
|
|
I don't like the behavior of the datagrid when using the celltemplate and cell editting template elements of WPF.
<DataGridTemplateColumn
Header="Street Name"
Width="77">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding street}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding street, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
You have to double tab to access the editing feature of the cell, you have to double click to edit it and it is when you use a combobox it gets even worse. Has anyone managed to make the DG usable? As it stands right now I need it, but I am almost embarrassed to release software with such erratic behavior. Does anyone have pointers on making this thing useful? My major issues are the double clicking issue with cellediting / celltemplate and the usability of a drop down:
<DataGridTemplateColumn
Header="State"
Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding PropertyState.name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.PropertyStates, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="name"
SelectedValuePath="id"
SelectedValue="{Binding stateId}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
If this thing is a pain, is there a useful method of validating the datagrid without using the celltemplate / celleditingtemplate?
Cheers, --EA
|
|
|
|
|
Thats probably just how the control works by default. If you don't like the behavior, you can always change it . I'm guessing you want a single click to go into edit mode? My first approach would be to catch the single click and if the cell contains a control, put it into edit mode. Lots and lots of gotchas though. A text box should go into edit mode directly on the first click, but a combobox should only go into edit mode if you click the edit portion. I don't think it should go into edit mode if you click the drop down button. Or maybe it should? You need to play with it. You'll probably have to special case a few of the control types to get what you want. I.e. if the control is a text box do this, if you the control is a combobox, do this instead. Another gotcha I can see is if you catch the click too early, you'll "break" the controls. For example, a push button should be executed on release, not on click. The click on a button just gives it focus and captures mouse focus so you can mouse off the button with the mouse still pressed and come back and it'll get pressed again, etc.
|
|
|
|
|
Having read SH01s answer and the fact that you rarely see this type of question (editing in a SL datagrid) I feel it reinforces a design decision I have made and advocate.
NEVER USE A LIST CONTROL AS AN EDIT CONTROL
I apply the same rule to winforms, 99% of the time I use a dedicated edit form/control to do editing, either pop a dialog or have a dedicated details control on the form. The ONLY exception I allow is when the user need to edit 1 (and only 1) single cell of either textbox or checkbox.
The combobox in SL is such a PITA I would never even consider using it in a gridview. Change you design!
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
So in this instance of a workspace based WPF desktop application would you consider a master / details layout in order to edit a row as opposed to in row editing? I fail to see why it would be discouraged to use a control that has editing functionality to edit, especially when it improves the user experience when working as intended.
Still, as it is not working as intended it is what it is. It would have been much easier for the user to allow in place editing.
Thanks for the insight, though.
Cheers, --EA
|
|
|
|
|
I don't really agree with Mycroft that you shouldn't allow in place row editing. Thats silly. Thats EXACTLY what a data grid is for. If you go into Excel and can't edit cells, you would be thouroughly confused. Just do what I suggested in my other response and tweak the control to your liking.
|
|
|
|
|
I think it is ridiculous to have to create a workaround for a dysfunctional control. In this particular issue, the "New Row" behavior of a datagrid is so erratic that I am hesitant to use it. The editing of existing rows is easy enough, but the adding a new row is a pain in the butt that I am almost willing to mitigate the pain of by adding a small control on the screen to insert new rows into the datagrid.
Has anyone had success with new rows? My issue is primarily that the comboxbox / textbox cellediting template leaves the textbox blank when you choose an item from the combobox in the following code:
<DataGridTemplateColumn
Header="State"
Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding PropertyState.name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.PropertyStates, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="name"
SelectedValuePath="id"
SelectedValue="{Binding stateId}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
After I choose an item from the drop down and leave the field it is a blank textbox.
|
|
|
|
|
Ok, first of all, before I get into your question, I'm guessing you haven't worked with WPF too long or extensively? Pretty much *ALL* the WPF controls are dysfunctional and immature when compared to the WinForms and Win32 versions. You have to remember that WPF controls are built from scratch from the ground up, not built on top of other technologies. I'm working on the ListView control at the moment because I needed to implement a lot of the missing features that came for free with the WinForms/Win32/MFC versions. So far, I've written about 3000 lines of C# code and about 1000 lines of XAML. That code is *NOT* sprinkled with 10 page comments or crazy white space or anything like that. That is ~4000 lines of straight up code to implement all the missing features in the ListView control. I would only need about 25% of this code if I was using Winforms or Win32. It sucks, sure, but thats part of playing with WPF . I'm sure MSFT will bring the controls up to snuff in the next few versions of WPF as some of the controls are missing really big features.
Anyways, with that being said...
Your template does look a bit suspicious to me, unless I'm missing something:
* Seems like you normally show PropertyState.name in the TextBlock
* Seems like your ComboBox shows a list of PropertyStates, displaying the name property with a property called stateId indicating the selected item
So...
1) you might want to get rid of the UpdateSourceTrigger=PropertyChanged and add Mode=TwoWay just to make sure that is working as expected... in certain situations the default values are different then what you might expect.
2) the big problem I see here is that assuming you are writing the combobox selection to stateId correctly as two-way, how exactly is the "PropertyState" and/or "PropertyState.name" notification happening? There isn't anything in your XAML that would indicate PropertyState.name is getting a notification change. I would think the ComboBox should write the selection to PropertyState no?
|
|
|
|
|
This is my first WPF project, you are correct there. I am not one hundred percent clear on the whole dependency property situation yet, so I am not entirely sure on how to update the PropertyState.name. Should I add an OnPropertyChanged("PropertyState") to the SelectedItem? I confess I am a little murky on one way - two way updates, the documentation for that has been vexing.
I will read more into it in hopes of scraping it together.
Cheers, --EA
|
|
|
|
|
Ok, well, theres basically two main ways you can tell the WPF data binding engine that something has changed.
1) Dependency Properties (or DP for short - the whole DependencyProperty.Register... mumbo jumbo)
2) INotifyPropertyChanged (or INPC for short)
These two methods are independent of each other. A DP doesn't use INPC and vice versa. A DP has its own infrastructure to notify of updates. INPC requires you to implement the PropertyChanged event and manually send out notifications.
Convention dictates that DPs are typically only used in controls and ViewModels use INPC.
DPs typically have C# get/set wrappers. It is important that the ONLY code you have there are the GetValue() and SetValue() calls. Thats it. Not one thing more. Not a single thing . Thats because only your code calls the getter & setter. The WPF infrastructure bypasses those completely. You don't want the user call and WPF doing different things.
With IPNC, WPF will call your C# get/set methods, so its consistent there.
Heres a link on binding modes: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx
What is "PropertyState" in your view model? Is that a DP or INPC?
I would think at the very least, you need to change to SelectedValue="{Binding PropertyState}" on the combo box. Not sure at all what "stateId" is all about.
|
|
|
|
|
PropertyState is really neither. It is actually a linked table to the item "SelectedItem" which is of type property. So the datagrid is bound to public ObservableCollection<property>AllProperties and PropertyState would be SelectedItem.PropertyState.name on a row by row basis. So it has no public characteristics of it's own, but is rather exposed through the logical element Property.
Top of the datagrid:
<DataGrid
ItemsSource="{Binding AllProperties}"
AutoGenerateColumns="False"
SelectedItem="{Binding SelectedItem}"
GridLinesVisibility="Horizontal"
CanUserDeleteRows="False"
Grid.Row="0"
RowStyle="{StaticResource RowStyle}">
ViewModel Items:
public Property SelectedItem
{
get { return _selectedItem; }
set
{
if (value == null)
return;
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
private void LoadProperties()
{
var query = _db.Properties.Where(p => p.claimId == _claimId && p.active);
AllProperties = new ObservableCollection<Property>(query.ToList());
}
private void LoadStates()
{
var query = _db.PropertyStates;
PropertyStates = new ObservableCollection<PropertyState>(query.ToList());
}
Does that make enough sense? I realize it is kind of a mess. SelectedItem<property> is an INPC.
|
|
|
|
|
If your combobox is showing PropertyStates, the SelectedItem is going to be a PropertyState. So you need to have an INPC of PropertyState. Both the combobox and the textblock need to bind to that property.
|
|
|
|
|
 Perhaps additional code will help to clarify.
public ObservableCollection<Property> AllProperties { get; private set; }
public ObservableCollection<PropertyState> PropertyStates { get; private set; }
public ObservableCollection<StreetSuffix> StreetSuffixes { get; private set; }
public ICommand SaveCommand { get { return new RelayCommand(param => Save(), CanSaveExecute); } }
public Property SelectedItem
{
get { return _selectedItem; }
set
{
if (value == null)
return;
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
public StreetSuffix SelectedSuffix
{
get { return _selectedSuffix; }
set
{
if (value == null)
return;
_selectedSuffix = value;
OnPropertyChanged("SelectedSuffix");
OnPropertyChanged("SelectedItem");
}
}
#endregion
#region private helpers
private void LoadProperties()
{
var query = _db.Properties.Where(p => p.claimId == _claimId && p.active);
AllProperties = new ObservableCollection<Property>(query.ToList());
}
private void LoadStreetSuffixes()
{
var query = _db.StreetSuffixes;
StreetSuffixes = new ObservableCollection<StreetSuffix>(query.ToList());
}
private void LoadStates()
{
var query = _db.PropertyStates;
PropertyStates = new ObservableCollection<PropertyState>(query.ToList());
}
And the view:
<DataGrid
ItemsSource="{Binding AllProperties}"
AutoGenerateColumns="False"
SelectedItem="{Binding SelectedItem}"
GridLinesVisibility="Horizontal"
CanUserDeleteRows="False"
Grid.Row="0"
RowStyle="{StaticResource RowStyle}">
<DataGrid.RowValidationRules>
<local:RowDataInfoValidationRule
ValidationStep="UpdatedValue" />
</DataGrid.RowValidationRules>
<DataGrid.CellStyle>
<Style
TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger
Property="IsSelected"
Value="True">
<Setter
Property="Foreground"
Value="WhiteSmoke" />
<Setter
Property="Background"
Value="LightSlateGray" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn
Header="Number"
Width="54">
<DataGridTextColumn.Binding>
<Binding
Path="number">
<Binding.ValidationRules>
<local:CellDataInfoValidationRule
ValidationStep="UpdatedValue" />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTemplateColumn
Header="Direction"
Width="58">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding direction}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding direction, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
Header="Street Name"
Width="77">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding street}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding street, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
Header="Type"
Width="55">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding StreetSuffix.abbreviation}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.StreetSuffixes}"
DisplayMemberPath="name"
SelectedValuePath="id"
SelectedValue="{Binding suffixId}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.SelectedSuffix}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn
Width="55"
Binding="{Binding unit}"
Header="Unit" />
<DataGridTemplateColumn
Header="City"
Width="75">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding city}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox>
<Binding
Path="city">
<Binding.ValidationRules>
<local:CellDataInfoValidationRule
ValidationStep="UpdatedValue" />
</Binding.ValidationRules>
</Binding>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
Header="State"
Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding PropertyState.name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.PropertyStates, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="name"
SelectedValuePath="id"
SelectedValue="{Binding stateId}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn
Header="Zip Code"
Width="60">
<DataGridTextColumn.Binding>
<Binding
Path="zip">
<Binding.ValidationRules>
<local:CellDataInfoValidationRule
ValidationStep="UpdatedValue" />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
So "Selected Item" refers to the selected item of the datagrid, not the selected item of the combobox. Is that my issue? That I need to bind to an ancestor?
|
|
|
|
|
It looks to me like the ComboBox is using this:
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.PropertyStates, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}"
So ComboBox is showing PropertyStates, that looks like ObservableCollection<PropertyState> to me, so its a collection of PropertyState objects. Thus each item in the ComboBox is a PropertyState. ComboBox.SelectedValue is going to be a PropertyState as well.
Right now you are binding ComboBox.SelectedValue to: SelectedValue="{Binding stateId}" . You aren't showing what stateId is.
The TextBlock is binding to "{Binding PropertyState.name}" which has to be a property of some sort on your ViewModel. You aren't showing that either.
So your problem is that your combobox.SelectedValue is binding to something completely different from the TextBlock, they need to bind to the same property so when you go to edit, you'll have the right item selected and when you are done editing, the TextBlock will have the correct text.
|
|
|
|
|
Binding StateId refers to the stateId of the datagrid's datacontext. Rather, it is a field on the property table, so it is one of the columns on SelectedItem<property>
So if this is all EF, as it is, then you have a Property Table and a PropertyState table with a FK - PK relationship. So if a property is in the state of CA, then the Property Table has stateId = 1, and PropertyState has Id:1|Name:California. Maybe I am too bound up in the WinForms world where the "Selected Value" of a combobox makes everything work automagically, but in this case my thought process was this:
{Binding PropertyState.name} simply gets the name of the state from the PropertyState table that belongs to whatever the stateID of the SelectedItem is. It obviously all works as it is, with the exception of a new item.
My thought on this was because the item does not yet have an item in the AllProperties collection, the UI is unable to resolve the relationship of Property (new) to PropertyState.
I could be way off base here. To maybe try a different tact, how would you deal with a datagrid in which you wanted to have a FK combo box column that constrains user choices, as in the scenario above? A simple address datagrid that has a drop down for State or Street Type or whatever?
|
|
|
|
|
I grabbed this piece of code, which I understand - Except for the "IsHitTestVisible="False" part.
What is a HitTest and what is the impact of setting/not setting this property?
<ContentControl Width="100"
Height="100"
Padding="1"
Canvas.Left="710"
Canvas.Top="300"
Style="{StaticResource DesignerItemStyle}">
<Ellipse IsHitTestVisible="False" >
<Shape.Fill>
<RadialGradientBrush Center="0.2, 0.2" GradientOrigin="0.2, 0.2" RadiusX="0.8" RadiusY="0.8">
<GradientStop Color="LightBlue" Offset="0"/>
<GradientStop Color="Blue" Offset="0.9"/>
</RadialGradientBrush>
</Shape.Fill>
</Ellipse>
</ContentControl>
Thanks
Everything makes sense in someone's mind
|
|
|
|