'Databinding complete' event for Silverlight 4.0 DataGrid? - data-binding

I have a DataGrid that I have bound to a property:
<cd:DataGrid
Name="myDataGrid"
ItemsSource="{Binding Mode=OneWay,Path=Thingies}"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
...
When the Thingies property changes, once all rows in the DataGrid have been populated with the new contents of Thingies, I want the DataGrid to scroll to the bottom row.
In WinForms, I would have done this by subscribing to the DataBindingComplete event. MSDN Forums contains several suggestions on how to do this with Silverlight 4.0 but they range from completely evil to just plain fugly:
start a 100ms timer on load, and scroll when it elapses
count rows as they're added, and scroll to the bottom when the number of added rows equals the number of entities in the data source
Is there an idiomatic, elegant way of doing what I want in Silverlight 4.0?

I stumbled upon this while searching for a resolution to the same problem. I was finding that when I attempted to scroll the selected item into view after filter and sort changes that I frequently received a run time error (index out of bounds). I knew instinctively that this was because the grid was not populated at that particular moment.
Aaron's suggestion worked for me. When the grid is defined, I add an event listener:
_TheGrid.LayoutUpdated += (sender, args) => TheGrid.ScrollIntoView(TheGrid.SelectedItem, TheGrid.CurrentColumn);
This solved my problem, and seems to silently exit when the parameters are null, too.

Why not derive from DataGrid and simply create your own ItemsSourceChanged event?
public class DataGridExtended : DataGrid
{
public delegate void ItemsSourceChangedHandler(object sender, EventArgs e);
public event ItemsSourceChangedHandler ItemSourceChanged;
public new System.Collections.IEnumerable ItemsSource
{
get { return base.ItemsSource; }
set
{
base.ItemsSource = value;
EventArgs e = new EventArgs();
OnItemsSourceChanged(e);
}
}
protected virtual void OnItemsSourceChanged(EventArgs e)
{
if (ItemSourceChanged != null)
ItemSourceChanged(this, e);
}
}

Use the ScrollIntoView method for achieving this.
myDataGrid.ItemSource = Thingies;
myDataGrid.UpdateLayout();
myDataGrid.ScrollIntoView(MyObservableCollection[MyObservableCollection.Count - 1], myDataGrid.Columns[1]);
You don't need to have any special event for this.

I think the nice way to do it, in xaml, is to have the binding NotifyOnTargetUpdated=true, and then you can hook the TargetUpdated to any event of your choice.
<ThisControl BindedProperty="{Binding xxx, NotifyOnTargetUpdated=true}"
TargetUpdated="BindingEndedHandler">

Related

Disabling every input on a page programmatically in ASP.NET

I have tried searching around for this, but what I found was mainly for disabling a single input type.
What I want to do is disable every input type on a single page. Everything. Textboxes, checkboxes the whole lot.
I couldnt figure out how to modify the loops I found, which is why I am asking here, beacause it's likely one of you has a piece of code laying around that can do it.
Thank you in advance.
try below if you like to do it in javascript/jquery
$(document).ready(function(){
$('input').attr('disabled','disabled');
});
OR try below if you want in asp.net
ach control has child controls, so you'd need to use recursion to reach them all:
protected void DisableControls(Control parent, bool State) {
foreach(Control c in parent.Controls) {
if (c is DropDownList) {
((DropDownList)(c)).Enabled = State;
}
DisableControls(c, State);
}
}
Then call it like so:
protected void Event_Name(...) {
DisableControls(Page,false); // use whatever top-most control has all the dropdowns or just the page control
} // divs, tables etc. can be called through adding runat="server" property

verifying when a property is set for a custom control

I'm hoping there is a pattern for verifying when a property is set for a custom control in asp.net.
Considering that there is a page life cycle, we keep having issues where the control can get into an invalid state. The best thing we can do is raise an exception with an instructional message for things like, setting values selected before adding data.
Please note, ideally the component wouldn't rely on things like ordering of when a property is set. Unfortunately I can only move the company to better practices one step at a time. There are too many components to re-write from scratch and is an unrealistic expectation.
That said, here's an example.
We have two properties. SelectedValues which will set the values that match a comma separated list and InsertAll which will insert "All" at the top of a list.
Potential issue: The developer sets the SelectedValues in the Page's PreInit event, but the InsertAll property, if true, will add the "All" value and select it during the control's Init event. The trick is, SelectedValues will directly set the values when set, not later during the life cycle. Which means, when they see the page, they think there is a bug in the component because they didn't set All to be selected, but it is.
Page:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
listBoxSelection.SelectedValues = "value1,value2";
listBoxSelection.InsertAll = true;
}
Control:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (this.InsertAll)
{
ListBoxItem allItem = new ListBoxItem()
{
Text = "All",
Value = "0"
Selected = true
};
this.Items.Insert(0, allItem);
}
}
How can I ensure that the developer knows they goofed when using this control? This is a simple example, but I see it all the time and in many forms.

DevExpress XtraGrid FocusedRowChanged event problem when changing datasource

This problem has bugged me for several years and maybe someone here knows a simple solution, since I just ran into it again.
QUESTION: Is there any way to get the XtraGrid to "forget" the current focused row index before a new (different) datasource is assigned to the grid?
BACKGROUND
We use the XtraGrid as a kind of controller for what is displayed in another panel of a multipane Winform.
Now imagine a hypothetical scenario where the datasource of the XtraGrid keeps changing according to menu selections. Menu item 1 populates the grid with a list of today's entrees in the cafeteria: Id, Name. Menu item 2 populates the grid with a list of Customers the user must phone that day: ID, Name. Important thing is that these are separate distinct datasources, and the grid's datasource is being assigned and reassigned.
CRITICAL FACT FOR THIS QUESTION:
We want the grid's FocusedRowChanged event to be the single place where we trap the user's selection in the controller grid. We are a "no spaghetti code" shop. FocusedRowChanged is better than a click event because it handles keyboard navigation too. The row with the focus contains the ID of the detail record we need to fetch from the database for display in Panel #2. This works--most of the time.
Here's how it doesn't work: let's say that on a given day, the list of customers the user must contact contains only one row. So the first (and only) row in the grid is the focused row. Now let's say that the user goes up to the menu and selects the menu item to display the day's cafeteria entrees. When the user clicks on the first item in the Entrees list, the FocusedRowChanged event does NOT fire because the grid has retained a memory of the focused row index from the previous datasource. The focused row index has not changed. And thus the user's selection doesn't trigger anything.
I tried to get DevExpress to offer a second more row-object-oriented mode (as distinct from row-index-oriented approach) whereby each row in the grid would have a GUID, and the FocusedRowChanged event would fire whenever the GUID of the currently focused row differed from the GUID of the previously focused row, regardless of whether the focused row index happened to be the same. This would allow dynamic changes of datasource and enable the desired behavior. But they demurred.
So I'll ask my question again, Is there any way to get the XtraGrid to "forget" the current focused row index before a new datasource is assigned to the grid?
Tim, I had the exact same problem when the grid only had one row of data in it and then changed data sources. I solved it by setting the gridview.FocusedRowHandle = -1 after setting the new datasource.
In a similar situation, I am subscribing to the
FocusedRowObjectChanged
event (using DevExpress 16.1).
I think that the best solution to this problem is to create a new GridView object and override its DoChangeFocusedRowInternal method. Below you will find the default implementation of this method. All you need to do is to change the marked row just as your needs dictate. Also, take a look at the How to create a GridView descendant class and register it for design-time use article, it contains some useful information.
public class MyGridView : GridView {
protected override void DoChangeFocusedRowInternal(int newRowHandle, bool updateCurrentRow) {
if(this.lockFocusedRowChange != 0) return;
if(!IsValidRowHandle(newRowHandle))
newRowHandle = DevExpress.Data.DataController.InvalidRow;
if(FocusedRowHandle == newRowHandle) return; // <<<<<<
int currentRowHandle = FocusedRowHandle;
BeginLockFocusedRowChange();
try {
DoChangeFocusedRow(FocusedRowHandle, newRowHandle, updateCurrentRow);
}
finally {
EndLockFocusedRowChange();
}
RaiseFocusedRowChanged(currentRowHandle, newRowHandle);
}
}
UPDATE
My code:
namespace MyXtraGrid {
public class MyGridControl : GridControl {
protected override BaseView CreateDefaultView() {
return CreateView("MyGridView");
}
protected override void RegisterAvailableViewsCore(InfoCollection collection) {
base.RegisterAvailableViewsCore(collection);
collection.Add(new MyGridViewInfoRegistrator());
}
}
public class MyGridViewInfoRegistrator : GridInfoRegistrator {
public override string ViewName { get { return "MyGridView"; } }
public override BaseView CreateView(GridControl grid) {
return new MyGridView(grid as GridControl);
}
}
public class MyGridView : GridView {
public MyGridView(GridControl ownerGrid) : base(ownerGrid) { }
public MyGridView() { }
protected virtual bool RowEqual(int focusedRowHandle, int newRowHandle) {
if(IsDesignMode)
return focusedRowHandle == newRowHandle;
DataRow row1 = GetDataRow(focusedRowHandle);
DataRow row2 = GetDataRow(newRowHandle);
return row1 == row2;
}
protected override void DoChangeFocusedRowInternal(int newRowHandle, bool updateCurrentRow) {
if(this.lockFocusedRowChange != 0) return;
if(!IsValidRowHandle(newRowHandle))
newRowHandle = DevExpress.Data.DataController.InvalidRow;
if(RowEqual(FocusedRowHandle, newRowHandle))
return;
int currentRowHandle = FocusedRowHandle;
BeginLockFocusedRowChange();
try {
DoChangeFocusedRow(FocusedRowHandle, newRowHandle, updateCurrentRow);
}
finally {
EndLockFocusedRowChange();
}
RaiseFocusedRowChanged(currentRowHandle, newRowHandle);
}
}
}
You can subscribe on the DataSourceChanged event which will fire when Data source changes (you guessed it!) so then you can get using GetFocusedObject() the object and display the relevant items for the other grid...

How to expand .NET TreeView node by clicking its text instead of +/-

I've been using hardcoded hyperlinks for my web app navigation, but the app has grown since and managing it is becoming a real pain. I've decided to replace what I have with the TreeView control, however I want to make several changes to the way it looks.
Is there any property that needs to be set, that would allow user to expand the TreeView node by clicking its text instead of +/- ?
I've already set ShowExpandColapse to 'false'.
I want my final result to end up as something similar to the TreeView on the left of the MSDN site.
Could anyone point me at the right direction please?
Set TreeNode.SelectAction to either Expand, or SelectExpand.
you can use xml data source or direct binding from db to treview
in the TreeView DataBound event we can write d recursive function as below to fetch each node and assign expand action to them.
protected void TreeView1_DataBound(object sender, EventArgs e)
{
foreach (TreeNode node in TreeView1.Nodes)
{
node.SelectAction = TreeNodeSelectAction.Expand;
PrintNodesRecursive(node);
}
}
public void PrintNodesRecursive(TreeNode oParentNode)
{
// Start recursion on all subnodes.
foreach(TreeNode oSubNode in oParentNode.ChildNodes)
{
oSubNode.SelectAction = TreeNodeSelectAction.Expand;
PrintNodesRecursive(oSubNode);
}
}
I think you just have to do this in code: handle the Click event, determine the currently-selected tree node, and toggle its Expanded property (I think that's what it's called here).
You can do this only this way! http://geekswithblogs.net/rajiv/archive/2006/03/16/72575.aspx
With respect,
Alexander

Where in the page lifecycle can I safely load/remove dynamic controls?

I'm working with dynamic fields in ASP.NET due to a very specifc and rigid end-user requirement that would take 2 hours just to explain. Suffice it to say, I can't make the requirement go away.
Anyway, I have a working solution in place; no problems with controls loading, rendering or maintaining their ViewState. This is what my OnLoad looks like:
public void override OnLoad(EventArgs e){
//don't need to check IsPostback, we have to load the controls on every POST
FormDefinition initialFormDefinition = ServiceLayer.GetFormDefinition(id);
BuildControls(initialFormDefinition);
}
In order to implement some biz logic around which dynamic fields are required, disabled or optional, I need to get the posted values (i.e. the ViewState) of my dynamic controls before I can actually add them to the page control hierarchy.
It's sort of a chicken/egg problem I suppose. ASP.NET won't automagically associate ViewState with the proper dynamic control until I've added them all to the page. On the other hand, I can't add these controls to the page until my service layer has applied biz rules that hinge on their current values. I tried to get around this rather unpleasant problem by writing this bit of pseudo-code :
public void override OnLoad(EventArgs e){
FormDefinition initialFormDefinition = ServiceLayer.GetFormDefinition(id);
BuildControls(initialFormDefinition);
if (IsPostBack){
PushControlValuesIntoForm(initialFormDefinition);
var updatedFormDefinition = ServiceLayer.ApplyBizRules(initialFormDefinition);
ReBuildControls(updatedFormDefinition); //remove controls and re-add them
}
}
Unfortunately, when you clear a control and re-add it, the ViewState is lost, even if the control type and ControlID are exactly the same, so this solution is a bust. Any reasonable ideas on how to accomplish what I'm after are welcome!
One way could be to load your controls and then decide if you need form definition to be be updated and if yes then re-initiate page life cycle again. See the below sample code:
public void override OnLoad(EventArgs e){
var updatedFormDef = Context.Items["UpdatedDef"] as FormDefinition;
if (null != updatedFormDef)
{
// Updated form def, rebuild controls
BuildControls(updatedFormDef);
}
else
{
// load initial form def
var initialFormDefinition = ServiceLayer.GetFormDefinition(id);
BuildControls(initialFormDefinition);
// check whether we need to update form def
if (IsPostBack){
PushControlValuesIntoForm(initialFormDefinition);
var updatedFormDefinition = ServiceLayer.ApplyBizRules(initialFormDefinition);
if (null != updatedFormDefinition)
{
// we have to update UI, transfer to self
Context.Items["UpdatedDef"] = updatedFormDefinition;
try
{
Server.Transfer(this.Request.RawUrl, true);
}
catch(ThreadAbortException)
{
// Do nothing
}
}
}
}

Resources