I am still new to silverlight and would like to ask few questions that relate to performing common tasks in silverlight that you used to do in asp.net programming (btw, I am using silverlight 4):
In silverlight, how do you access a public property on a user control in a databinding expression (without setting datacontext on the control itself)? For example, let us use a datagrid with ItemSource bound to some collection but you want to also databind to a value defined by a property your user control using the databinding expression, perhaps using 'Source' property. In asp.net you could access any public property/method using <%# expr #>.
In asp.net when a postback control was clicked and raised an event you were able to access the row in the event handler via event args and use FindControl() to find any control in the row. What's the equivalent process in silverlight?
I know how to do get the row using DataGridRow.GetRowContainingElement() but then when I use row.FindName() I can't find another control in the same row by its name, I get null back. I found postings to do something like: grid.columns[colIndex] but that's error prone since you are using index to reference the column and then you have to get the cell content to access the control you after (cell.GetCellContent(row)). It is also not universal, the above illustrated how to do it in a datagrid.
In asp.net there's OnDataBind event you can handle on majority controls, is there something equivalent in silverlight?
to create another property like the datacontext for your user control, you can create your own custom dependency property.
I would use the SelectionChanged event instead of the mouse click. it will easily tell you what row was "Added" when the user clicked the row.
At this time, no, you do not have a DataContext_Changed event in silverlight. BUT you can create your own by creating a custom dependency property, which will set the data context, and raise your own custom events. (not really sure why they didn't implement that originally, its in the WPF world).
edit to bind a property to the current control, use the following format:
Property="{Binding RelativeSource={RelativeSource Self}, Path=YourCustomProperty}"
for example, here is a textbox, where its text property is bound to its ID property:
<TextBox Height="16" HorizontalAlignment="Left" Margin="97,105,0,0" Name="txtName"
VerticalAlignment="Top" Width="120"
Text="{Binding RelativeSource={RelativeSource Self}, Path=Name}"/>
Related
I have defined a UserControl for DateTime picking in Windows 8 store apps. The control consists of 3 checkboxes and hast a property to channel out the selected date time.
When I include this control into another UserControl and name it, I am not able to access it from C# code.
//...Page content....
<TextBlock Text="Erledigen bis:" FontSize="16"/>
<local:DateTimePicker Name="dtp_dueUntil" />
<TextBlock Text="Wichtigkeit" FontSize="16"/>
//...Page content....
*dtp_dueUntil* is not known in my code behind file.
Am I doing something awefull wrong, or just missing a point here?
You should not access controls like that, unless you have no other choice. In your case, iF you what to expose selected DateTime in your first user control, then simply declare a Dependency Property which will hold and update this value (via DataBinding or event).
I have a basic project in WPF.
All it does it retrieve / update products.
As shown in the image below, the user enters an ID, the data is then displayed according to it, and the user is able to change the data and click 'Save Product' to save it to the database.
The GetProduct(int id) function retrieves a product by the ID provided.
The SaveProduct() function saves the changed fields.
Also, there are two DataTemplates:
1) For the ProductModel - includes 3 textboxes: ProductId, ProductName, UnitPrice.
2) For the ProductViewModel - includes the save/get buttons + a textbox for the user to enter the id of the desired product.
What I'm trying to do is get the changed data when a user clicks the 'Save Product' button.
The most ideal way in my opinion, is to use Binding.
Each textbox is already binded, but I have no idea how to get the binded data.
Here is an example of a binded textbox in the FIRST DataType (ProductModel):
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding ProductId}" Margin="5" Width="150" />
There is one for each of the following properties: ProductId, ProductName and UnitPrice.
IMPORTANT!: The Get/SaveProduct() functions are in the ProductViewModel class, while the actual product class is - you guessed it - ProductModel. The ProductViewModel class holds a variable that contains the current product displayed.
This is the button that's used to save the info - it is written in the SECOND DataType (ProductViewModel):
<Button Content="Save Product" DockPanel.Dock="Right" Margin="10,2" VerticalAlignment="Center" Command="{Binding Path=SaveProductCommand}" Width="100" />
The SaveProductCommand command simply fires the SaveProduct() function.
I have a few questions regarding this whole subject:
What does it mean when a binding is used like this : {Binding ProductId} ?
The default binding mode for textboxes is TwoWay as far as I remember. But in this case, ProductId/Name + UnitPrice are not dependency properties, therefore is it right that the binded values do not update/sent back when the text in the textboxes is changed? (Since there isn't an event attached to it...)
A data context was never configured in my project, but all of the "binding tags" in my XAML pages don't seem to have a defined source. Could it be that the source is actually the DataType in the DataTemplate that includes the binded objects?
The SECOND DataTemplate (the ProductViewModel one) has this ContentControl tag: <ContentControl Margin="10" Content="{Binding Path=CurrentProduct}" />.
What is it's purpose?
If a TwoWay binding were/does occur, how do I get the values from within the SaveProduct() function? Do I just refer to, say CurrentProduct.ProductName to get the changed name?
Much thanks to everyone who takes their time to answer - I appreciate it so much!
What does it mean when a binding is used like this : {Binding
ProductId} ?
The specific control property you have this binding set on is going to look for the ProductId property on the object set as the DataContext and set the propertys value in the control accordingly.
The default binding mode for textboxes is TwoWay as far as I remember.
But in this case, ProductId/Name + UnitPrice are not dependency
properties, therefore is it right that the binded values do not
update/sent back when the text in the textboxes is changed? (Since
there isn't an event attached to it...)
You do not need to make the properties within your object a DependencyProperty for TwoWay binding to occur.
A data context was never configured in my project, but all of the
"binding tags" in my XAML pages don't seem to have a defined source.
Could it be that the source is actually the DataType in the
DataTemplate that includes the binded objects?
The bindings being set within your XAML will use the object stored within the DataContext, thus if you do not explicitly set the DataContext of the view, it will be null. You should note however that the DataContext is inherited from its parent. If you are in fact setting the content by using say, CurrentProduct, then all the properties will be available to bind to per your Product type.
The SECOND DataTemplate (the ProductViewModel one) has this
ContentControl tag:
<ContentControl Margin="10" Content="{Binding Path=CurrentProduct}" />
What is it's purpose?
It is acting as the container of your CurrentProduct, which can contain one and only one item.
If a TwoWay binding were/does occur, how do I get the values from
within the SaveProduct() function? Do I just refer to, say
CurrentProduct.ProductName to get the changed name?
Without seeing the entire application, my guess is that the ContentControl is being set to the CurrentProduct and your TextBox, etc.. are all bound to the respective properties, such as CurrentProduct.ProductId, etc... The product which you want to save is in fact the CurrentProduct. When you call save within your ViewModel, you simply access the CurrentProduct and persist it as needed, where CurrentProduct.PropertyName will contain the changes which were propagated from the UI.
Is it possible to make a table having cells bound to several objects (for example, textboxes) without making use of DataGrid?
Example:
<TextBox Text="{Binding Path=FileName}" Width="300"></TextBox>
The DataContext for the textbox's container should contain a Property named FileName
You should note that your property should be wired to notify when it is changed. See the following for more information:
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
How to focus a textbox from ViewModel wpf?
<TextBox Name="PropertySearch"
Text="{Binding UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay, Path=PropertySearch,
ValidatesOnDataErrors=True}"
Width="110"
Height="25"
Margin="10" />
You can do this by adding a property to your ViewModel (or use an existing property) that indicates when the SetFocus should happen but the View should be responsible for actually setting the focus since that is purely View related.
You can do this with a DataTrigger.
View:
<Grid Name="LayoutRoot" DataContext="{StaticResource MyViewModelInstance}">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding UserShouldEditValueNow}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=PropertySearch}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBox Name="PropertySearch" Text="{Binding UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Path=PropertySearch, ValidatesOnDataErrors=True}" Width="110" Height="25" Margin="10" />
</Grid>
ViewModel:
// When you think the view should set focus on a control
this.UserShouldEditValueNow = true;
The example above is simplified by just using a boolean ViewModel property "UserShouldEditValueNow". You can add a property like this to your ViewModel or use some other exising property that indicates this state.
Note: So why is it done this way in MVVM? One reason is, suppose the View author decided to replace the TextBox with a ComboBox, or even better, suppose your property was an integer value that had both a TextBox to view/edit the number and a Slider as another way to edit the same value, both controls bound to the same property... how would the ViewModel know which control to set focus on? (when it shouldn't even know what control, or controls, are bound to it in the first place) This way the View can select which control to focus by changing the ElementName binding target in the DataTrigger Setter.
Happy coding!
The question you should be asking yourself is "why does my ViewModel need to know which control has the focus?"
I'd argue for focus being a view-only property; it's an interaction property, and has nothing to do with the conceptual state. This is akin to the background color of a control: why would you represent it in the VM? If you need to manage the focus in a custom way, it's probably better to use a view-level object to do the job.
In your parent control, add the following property:
FocusManager.FocusedElement="{Binding ElementName=PropertySearch}"
While purists may argue for leaving this out of the VM, there are cases where it may make sense to do so from the VM.
My approach has been to make the view implement an interface, pass that interface to the ViewModel, and then let the VM call methods on the interface.
Example:
public interface IFocusContainer
{
void SetFocus(string target);
}
A couple things to keep in mind:
A VM might serve more than one instance of a view, so your VM might want to have a collection of references to IFocusContainer instances, not just one.
Code the VM defensively. You don't know whether there are 0, 1 or 20 views listening.
The "target" parameter of SetFocus() should probably be "loosely" coupled to the VM. You don't want the VM caring about the exact control names in the UI. Rather, the VM should indicate a name that is defined solely for focus management. In my case, I created some attached properties that would allow me to "tag" controls with "focus names".
To implement the interface, you can:
Implement it in the code-behind
Create some behaviors that know how to attach to the ViewModel that is present in the DataContext.
There's nothing wrong with implementing it on the Code Behind, but the behavior approach does allow a XAML only hookup if that's important to you.
In the implementation of the interface, you can use the visual tree to locate the control, or you could just code up a switch statement for a known set of focusable items.
Given:
<StackPanel>
<View:ArcController x:Name="control1" Visibility="{Binding Path=CanShowDateControl, Converter={StaticResource bool2VisibilityConverter}}" />
<my1:DateLabelView x:Name="control2" DataContext="{Binding Path=DateLabelViewModel}" Visibility="{Binding ElementName=ctrlTableToolbar, Path=DataContext.IsDateReadOnly, Converter={StaticResource bool2VisibilityConverter}}" />
</StackPanel>
I have two controls (control1, and control2) inside a stackpanel, and at one time i want to show only one of the controls.
As shown in the code, the visibility of the controls is driven by "IsDateReadOnly" and "CanShowDateControl".
And, as per my viewmodel logic... CanShowDateControl = !IsReadOnly.
So, at one time I will ONLY show one of the two controls.
Question: My problem is, though i am showing only one control at a time, my xaml is creating instance of both the controls. Is it possible to create instance of only the control that i am showing?
Give that:
1) I want to show/hide using binding, so that logic lies in my viewmodel.
2) I can keep these two controls inside one wrapper control. Since i am using it at different places.
Thanks for your interest.
Use a ContentControl and ContentTemplateSelector with two DataTemplates. One for ReadOnly and other for Not ReadOnly.
In the selector, based on the property, return appropriate DataTemplate.
Other way you could go is create a Custom Control which has two (or more if more than two) properties to store two controls. Based on a condition, it should add one of them to the Visual Tree which will prevent the other one from being loaded.