|
1) Something like this perhaps?
<wpftk:DataGridComboBoxColumn EditingElementStyle="{StaticResource MyComboBoxstyle}" />
(or try ElementStyle=...)
2) Try setting the Width property on the last column to "*".
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
I have a Silverlight 3.0 application which has the following:
listbox listbox grid (editContainer)
Customers Locations 3 comboboxes
2 textboxes
2 buttons
At startup the customer listbox is populated -- nothing else! At this point all of the controls in the editContainer are unaccessible...as if they are in a disabled state.
When a customer is selected the customer Locations list is populated, 2 comboboxes are populated. I've walked through this in debug mode and confirmed that the comboboxes are indeed getting data. The combo boxes are using code from another site that inherits the base silverlight code and adds the population logic when a database item has the key value instead of the text value in the combo box.
When I get the message that I can now edit data, everything in the editContainer is still in a disabled state. ComboBoxes don't drop down, cannot get my cursor entered into the textbox, and my buttons don't fire events. I initially thought it was because I added code to set the selected index on the combo boxes to -1, but even after commenting all that out I still have read-only controls.
here is the xaml:
<navigation:Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:dataInput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input"
xmlns:DatabaseManager_Silverlight_MasterService="clr-namespace:DatabaseManager.Silverlight.MasterService"
xmlns:DatabaseManager_Silverlight="clr-namespace:DatabaseManager.Silverlight"
xmlns:mycompany_Silverlight_Controls="clr-namespace:mycompany.Silverlight.Controls;assembly=mycompany.Silverlight.Controls"
x:Class="DatabaseManager.Silverlight.Views.ManageCustomerLocations"
d:DesignWidth="800" d:DesignHeight="600"
Title="ManageCustomerLocations Page">
<Grid x:Name="LayoutRoot" Background="#FFF8F8DE">
<Grid x:Name="editContainer" Margin="8,20,8,6">
<ListBox x:Name="customerList" HorizontalAlignment="Left" Margin="0,16,0,29" Width="183" SelectionChanged="CustomerSelectedEventHandler" DisplayMemberPath="CustomerName" />
<TextBox x:Name="message" Height="23" VerticalAlignment="Bottom" IsReadOnly="True" Text="{}{messages will appear here}" TextWrapping="Wrap"/>
<ListBox x:Name="customerLocations" HorizontalAlignment="Left" Margin="187,16,0,29" Width="100" SelectionChanged="CustomerLocationSelectedEventHandler" DisplayMemberPath="LocationID" />
<dataInput:Label HorizontalAlignment="Left" Margin="38,0,0,0" VerticalAlignment="Top" Width="72" Content="Customers" FontWeight="Bold" Foreground="#FF0D0D39" RenderTransformOrigin="1.153,0.562"/>
<dataInput:Label HorizontalAlignment="Left" Margin="204,0,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.567,1.562" Width="63" Content="Locations" FontWeight="Bold" Foreground="#FF0D0D39"/>
<Grid x:Name="locationContainer" Margin="291,16,0,29" IsHitTestVisible="False">
<Grid.DataContext>
<DatabaseManager_Silverlight_MasterService:CustomerLocation/>
</Grid.DataContext>
<Button x:Name="bmcKeyComputation" Margin="21,0,0,133" Content="Compute BMC Key" Click="ComputeBmcKeyEventHandler" Height="25" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="132"/>
<Button x:Name="submitChanges" Margin="21,0,31,65" VerticalAlignment="Bottom" Content="Create Location" Click="PostLocationEventHandler" ToolTipService.ToolTip="This button will create/update the location record."/>
<dataInput:Label HorizontalAlignment="Left" Margin="21,0,0,181" VerticalAlignment="Bottom" Content="Location Identity:" d:LayoutOverrides="HorizontalAlignment"/>
<TextBox x:Name="locationIdentity" HorizontalAlignment="Right" Margin="0,0,31,173" TextWrapping="Wrap" VerticalAlignment="Bottom" DataContext="{Binding LocationID, Mode=TwoWay, UpdateSourceTrigger=Explicit}" ToolTipService.ToolTip="This is the name for the location (office number, cage ID, etc)"/>
<TextBox x:Name="bmcKey" HorizontalAlignment="Right" Margin="0,0,31,134" TextWrapping="Wrap" VerticalAlignment="Bottom" DataContext="{Binding BmcLocKey, Mode=TwoWay, UpdateSourceTrigger=Explicit}" IsReadOnly="True" ToolTipService.ToolTip="The resulting BMC Key generated from the previous location data"/>
<dataInput:Label Height="20" VerticalAlignment="Top" Content="Building :" HorizontalAlignment="Left" Width="71" Margin="21,63,0,0"/>
<dataInput:Label Height="21" Margin="21,104,0,0" VerticalAlignment="Top" Content="Data Center :" HorizontalAlignment="Left" Width="80"/>
<dataInput:Label Height="19" Margin="21,146,0,0" VerticalAlignment="Top" Content="Location Type :" HorizontalAlignment="Left" Width="89"/>
<mycompany_Silverlight_Controls:ComboBoxClassic x:Name="buildingList" Margin="123,63,31,0" VerticalAlignment="Top" DisplayMemberPath="{Binding Building, Mode=TwoWay, UpdateSourceTrigger=Default}" ToolTipService.ToolTip="Identifies the starting point for this location"/>
<mycompany_Silverlight_Controls:ComboBoxClassic x:Name="dataCenterList" Margin="123,104,31,0" VerticalAlignment="Top" ToolTipService.ToolTip="Provides the secondary component of the location -- this changes based on 'building'" DataContext="{Binding DataCenter, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
<mycompany_Silverlight_Controls:ComboBoxClassic x:Name="locationTypeList" Margin="123,146,31,0" VerticalAlignment="Top" ToolTipService.ToolTip="For true datacenter locations, this is the type of location the customer contracted" DataContext="{Binding LocationType, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
</Grid>
</Grid>
</Grid>
</navigation:Page>
It's alot of code, but here is the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;
using DatabaseManager.Silverlight.MasterService;
namespace DatabaseManager.Silverlight.Views
{
public partial class ManageCustomerLocations : Page
{
#region Page initialization logic
public ManageCustomerLocations( )
{
InitializeComponent( );
buildingList.DisplayMemberPath = "BuildingName";
buildingList.SelectedValuePath = "BuildingIdentity";
buildingList.BindingValidationError+=new EventHandler<ValidationErrorEventArgs>(buildingList_BindingValidationError);
dataCenterList.DisplayMemberPath = "DataCenterText";
dataCenterList.SelectedValuePath = "DataCenterIdentity";
dataCenterList.BindingValidationError+=new EventHandler<ValidationErrorEventArgs>(dataCenterList_BindingValidationError);
locationTypeList.DisplayMemberPath = "LocationName";
locationTypeList.SelectedValuePath = "LocationType";
locationTypeList.BindingValidationError+=new EventHandler<ValidationErrorEventArgs>(locationTypeList_BindingValidationError);
locationContainer.DataContext = new CustomerLocation( );
}
void locationTypeList_BindingValidationError( object sender, ValidationErrorEventArgs e )
{
MessageBox.Show( e.Error.Exception.Message );
}
void buildingList_BindingValidationError( object sender, ValidationErrorEventArgs e )
{
MessageBox.Show( e.Error.Exception.Message );
}
void dataCenterList_BindingValidationError( object sender, ValidationErrorEventArgs e )
{
MessageBox.Show( e.Error.Exception.Message );
}
bool InUpdateState = false;
protected override void OnNavigatedTo( NavigationEventArgs e )
{
PopulateCustomerList( );
}
private void PopulateCustomerList( )
{
DataReflectorClient client = new DataReflectorClient( );
client.OpenAsync( );
client.GetActiveCustomersCompleted += new EventHandler<GetActiveCustomersCompletedEventArgs>( ActiveCustomersEventHandler );
client.GetActiveCustomersAsync( );
client.CloseAsync( );
message.Text = "Loading active customers at this moment...please wait.";
}
#endregion
#region Database Completion Event Handlers
void GetCustomerLocationsCompletedEventHandler( object sender, GetCustomerLocationsCompletedEventArgs e )
{
customerLocations.ItemsSource = e.Result;
if ( e.Error != null )
SetWarningText( e.Error.Message );
else
SetEndingText( "Customer locations loaded successfully." );
customerLocations.UpdateLayout( );
}
void ActiveCustomersEventHandler( object sender, GetActiveCustomersCompletedEventArgs e )
{
this.customerList.ItemsSource = e.Result;
SetEndingText( "You may now choose the customer you want to work on." );
}
void GetLocationsCompleteEventHandler( object sender, GetLocationTypesCompletedEventArgs e )
{
locationTypeList.ItemsSource = e.Result;
SetEndingText( "Dropdown boxes populated." );
locationTypeList.UpdateLayout( );
}
void GetBuildingsCompleteEventHandler( object sender, GetBuildingEnumeratorsCompletedEventArgs e )
{
buildingList.ItemsSource = e.Result;
SetEndingText( "Dropdown boxes populated." );
buildingList.UpdateLayout( );
}
void LoadDataCenterEventHandler( object sender, GetDatacenterEnumeratorsCompletedEventArgs e )
{
dataCenterList.ItemsSource = e.Result;
SetEndingText( "You may now select which datacenter the customer location is at." );
dataCenterList.UpdateLayout( );
}
void HandleUpdateCompleteEventHandler( object sender, ManageCustomerLocationCompletedEventArgs e )
{
MessageResults results = e.Result as MessageResults;
if ( results == null )
SetWarningText( String.Format( "Unexpected result sent to completion event. Type of result was : {0}", e.Result.GetType( ).ToString( ) ) );
else if ( results.Status.Equals( 1 ) )
{
message.Text = String.Format( "Results: {0}", results.Message );
}
else
message.Text = String.Format( "Error: {0} Reason : {1}.....ooops.", results.ErrorMessage, results.Reason );
selectedLocation = null;
}
#endregion
#region Selection Event Handler Implementations
private void CustomerSelectedEventHandler( object sender, SelectionChangedEventArgs e )
{
customerLocations.ItemsSource = null;
if ( customerList.SelectedIndex < 0 )
{
return;
}
CustomerMaster master = customerList.SelectedItem as CustomerMaster;
if ( master == null )
{
SetWarningText( "There is a problem getting access to the selected customer." );
return;
}
DataReflectorClient client = new DataReflectorClient( );
client.OpenAsync( );
client.GetCustomerLocationsCompleted += new EventHandler<GetCustomerLocationsCompletedEventArgs>( GetCustomerLocationsCompletedEventHandler );
client.GetCustomerLocationsAsync( master.CustomerIdentity );
InUpdateState = false;
SetWarningText( "Populating dropdown boxes. Please wait...." );
client.GetBuildingEnumeratorsCompleted += new EventHandler<GetBuildingEnumeratorsCompletedEventArgs>( GetBuildingsCompleteEventHandler );
client.GetBuildingEnumeratorsAsync( );
client.GetLocationTypesCompleted += new EventHandler<GetLocationTypesCompletedEventArgs>( GetLocationsCompleteEventHandler );
client.GetLocationTypesAsync( );
client.CloseAsync( );
}
CustomerLocation selectedLocation = null;
private void CustomerLocationSelectedEventHandler( object sender, System.Windows.Controls.SelectionChangedEventArgs e )
{
if ( customerLocations.SelectedIndex < 0 )
{
}
else
{
InUpdateState = true;
selectedLocation = customerLocations.SelectedItem as CustomerLocation;
locationContainer.DataContext = selectedLocation;
SetEndingText( String.Format( "Chosen location {0} is now loaded into the editor.", selectedLocation.LocationID) );
}
}
private void BuildingSelectedEventHandler( object sender, SelectionChangedEventArgs e )
{
if ( buildingList.SelectedIndex < 0 ) return;
SetWarningText( "Getting the datacenter locations for selected building location." );
DataReflectorClient client = new DataReflectorClient( );
client.OpenAsync( );
client.GetDatacenterEnumeratorsCompleted += new EventHandler<GetDatacenterEnumeratorsCompletedEventArgs>( LoadDataCenterEventHandler );
client.GetDatacenterEnumeratorsAsync( ( ( BuildingEnumerator )buildingList.SelectedItem ).BuildingIdentity );
client.CloseAsync( );
}
private void DataCenterSelectedEventHandler( object sender, System.Windows.Controls.SelectionChangedEventArgs e )
{
if ( dataCenterList.SelectedIndex < 0 ) return;
SetEndingText( "You may now select the location type if applicable to this location." );
}
private void locationTypeList_SelectionChanged( object sender, SelectionChangedEventArgs e )
{
}
#endregion
#region Button Event handler implementations
private void ComputeBmcKeyEventHandler(object sender, System.Windows.RoutedEventArgs e)
{
message.Text = string.Empty;
BuildingEnumerator building = buildingList.SelectedItem as BuildingEnumerator;
if ( building == null )
message.Text = "You appear to have not selected a building yet. You need a building location to compute a BMC Key.";
else if ( locationIdentity.Text.Length.Equals( 0 ) )
message.Text = "You appear to have not provided a location name. Enter the location name before computing the BMC Key.";
else
bmcKey.Text = String.Concat( building.BuildingName.Substring( 0, 5 ), locationIdentity.Text );
}
private void PostLocationEventHandler(object sender, System.Windows.RoutedEventArgs e)
{
bool NoErrorsExist = true;
BuildingEnumerator building=null;
message.Text = string.Empty;
if ( buildingList.SelectedItem == null )
{
message.Text += "No building selected. ";
NoErrorsExist = false;
}
else
building = (BuildingEnumerator)buildingList.SelectedItem;
if ( dataCenterList.SelectedItem == null )
{
message.Text += "No data center selected. ";
NoErrorsExist = false;
}
if ( locationTypeList.SelectedItem==null && building != null && building.BuildingName != "Exacent" && building.BuildingName!="Office" )
{
message.Text += "No location type selected. ";
NoErrorsExist = false;
}
if ( locationIdentity.Text.Length.Equals( 0 ) )
{
message.Text = "No location name given. ";
NoErrorsExist = false;
}
if ( bmcKey.Text.Length.Equals( 0 ) && building != null && building.BuildingName != "Exacent" && building.BuildingName != "Office" )
{
message.Text = "BMC Key has not been generated. ";
NoErrorsExist = false;
}
if ( NoErrorsExist )
{
DataReflectorClient client = new DataReflectorClient( );
CustomerLocation newLocation = new CustomerLocation( );
if ( customerLocations.SelectedIndex > 0 )
newLocation = ( CustomerLocation )customerLocations.SelectedItem;
client.OpenAsync( );
if ( InUpdateState )
{
if ( newLocation.Building != ( ( BuildingEnumerator )buildingList.SelectedItem ).BuildingIdentity )
newLocation.Building = ( ( BuildingEnumerator )buildingList.SelectedItem ).BuildingIdentity;
if ( newLocation.DataCenter != ( ( DataCenterEnumerator )dataCenterList.SelectedItem ).DataCenterIdentity )
newLocation.DataCenter = ( ( DataCenterEnumerator )dataCenterList.SelectedItem ).DataCenterIdentity;
if ( newLocation.LocationType.HasValue && newLocation.LocationType.Value != ( ( LocationEnumerator )locationTypeList.SelectedItem ).LocationType )
newLocation.LocationType = ( ( LocationEnumerator )locationTypeList.SelectedItem ).LocationType;
if ( newLocation.LocationID != locationIdentity.Text )
newLocation.LocationID = locationIdentity.Text;
if ( newLocation.BmcLocKey != bmcKey.Text )
newLocation.BmcLocKey = bmcKey.Text;
client.ManageCustomerLocationCompleted += new EventHandler<ManageCustomerLocationCompletedEventArgs>( HandleUpdateCompleteEventHandler );
client.ManageCustomerLocationAsync( newLocation, RequestType.Update );
client.CloseAsync( );
}
else
{
CustomerLocation newlocation = new CustomerLocation();
newlocation.LocationIdentity = Guid.NewGuid( );
newlocation.CustomerIdentity = ( ( CustomerMaster )customerList.SelectedItem ).CustomerIdentity;
newlocation.Building = ( ( BuildingEnumerator )buildingList.SelectedItem ).BuildingIdentity;
newlocation.DataCenter = ( ( DataCenterEnumerator )dataCenterList.SelectedItem ).DataCenterIdentity;
newlocation.LocationType = locationTypeList.SelectedIndex >= 0 ? ( ( LocationEnumerator )locationTypeList.SelectedItem ).LocationType : new Nullable<int>( );
newlocation.LocationID = locationIdentity.Text;
newlocation.BmcLocKey = bmcKey.Text;
client.ManageCustomerLocationCompleted += new EventHandler<ManageCustomerLocationCompletedEventArgs>( HandleUpdateCompleteEventHandler );
client.ManageCustomerLocationAsync( newlocation, RequestType.Create );
client.CloseAsync( );
}
}
else
{
message.Text += "Please correct errors.";
return;
}
}
#endregion
void SetWarningText( string text )
{
SolidColorBrush brush = new SolidColorBrush( Colors.Red );
message.Foreground = brush;
message.Text = text;
}
void SetEndingText( string text )
{
SolidColorBrush brush = new SolidColorBrush( Colors.Black );
message.Foreground = brush;
message.Text = text;
}
}
}
|
|
|
|
|
Michael Eber wrote:
<Grid x:Name="locationContainer" Margin="291,16,0,29" IsHitTestVisible="False">
There's your problem right there.
IsHitTestVisible applies to the object and its children, and prevents it from responding to mouse events.
|
|
|
|
|
OMG! Thank you so much. I've been on app so long I'm seeing the forest and missing the trees.
You saved several more hours of hunting for this!!!
|
|
|
|
|
No problem
You stare at your own code for too long, you start to focus on some parts and disregard others... Then all it takes is a fresh pair of eyes to find the piece you ignored.
When I think I'm getting to this point, I usually hop on CP or /. for a bit to reboot the old brain.
|
|
|
|
|
|
|
Do you mean html page? Use javascript. Detailed info can be found at:
http://pietschsoft.com/post/2008/06/Silverlight-and-JavaScript-Interop-Basics.aspx
April
Comm100 - Leading Live Chat Software Provider
modified 27-May-14 21:45pm.
|
|
|
|
|
I have an object tree that has a few different objects types. For each of the object types, I would like them to be instantiated in a treeview with a custom element derived from the treeviewitem. Can someone give me a pointer for getting started with this approach?
I am aware of the simplicity of dealing with a treeview through an object model such as noted in this article[^]. However, this approach does not allow handling routed commands or creating command bindings. It also attaches a model object to a UI element, which complicates the application design if more than one UI element needs to refer to the model item. In my case, I really want the UI cleanly separated from the model. Again, some good pointers would be greatly appreciated.
|
|
|
|
|
Creating derived types for controls in WPF is usually a bad idea. Have you heard of DataTemplates or DataTemplateSelectors? You can use the former when you want to use a template according the the type of data (e.g., you can display class Cat differently than class Duck) and you can use the latter to select the template based on custom logic (e.g., if the name of the animal is "Alfred" or the previous animal was a Duck and the current one is a Dog, then display a particular template). I suggest you read Dr. WPF's Take on Data Templates.
|
|
|
|
|
I'm looking for information on a more specific issue than your largely unrelated response seems to recognize.
Yes, I am familar with templating, template seletors and what they can/cannot do. The article linked in the original posting and the specific issue of implementing routed command handlers within the individual treeviewitems illuminate the primary issue that I am trying to address. If you have suggestions on effective techniques to solve that sort of issue, your input will be warmly received.
With respect to blandly identifying good or bad approaches for UI development within WPF, I recommend that you review the "Control Authoring Overview" in MSDN. It specificially highlights when to write a new control and the capabities and limitations imposed by the level within the class heirarchy one chooses to base there development.
|
|
|
|
|
I can be more specific if you can be more specific. Here are some questions you can answer that may lead to more specific answers for you.
hb52134214 wrote: this approach does not allow handling routed commands or creating command bindings
Sure it does. Why wouldn't it? What are you trying to accomplish and what problems are you running into?
hb52134214 wrote: complicates the application design if more than one UI element needs to refer to the model item
Again, not sure I follow. Can you give an example of how multiple UI elements referring to the same model item complicates things? I'm envisioning a TextBlock and a TextBox binding to the same item... I don't see a problem with that.
hb52134214 wrote: I really want the UI cleanly separated from the model
Some people suggest MVVM. However, I never really have liked the concept of a view model. For more complex models (custom trees and such), rebuilding that as a view model seems like a lot of work. Sometimes, though, it is really easy to create a model and a view model. I personally don't stick to any particular design pattern religiously... I just try them and use them when I feel it's appropriate.
|
|
|
|
|
With regard to routed commands:
Lets say my application has two (bubbling) routed commands: SaveFile and SaveProject. The application also has a tree structure that represents a nested heirarchy of files and projects (and maybe other stuff as well). Lets say I have the following trivial heirarchical data templates (HDT):
<HDT x:Key="pItem" DT="{x:Type c:Project}" IS="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HDT>
<HTD x:Key="fItem" DT="{x:Type c:File}" IS="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HDT>
<HTD x:Key="other" DT="{x:Type c:Project}" IS="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HDT>
How does one attach a routed command handler in the template to get a node to respond to a SaveFile command. In anything else, I'd throw in the basic:
<CommandBindings>
<CommandBinding Command="{x:Static c:Cmds.SaveFile}" CanExecute="CanSave" Execute="Save"/>
</CommandBindings>
Unfortunately, I haven't been able to achieve this simple solution with a Heirarchical data template. Any suggestions?
On a side note, the ItemsControl (and thus TreeView & TreeViewItem) has a virtual function GetContainerForItemOverride, which allows one to return the container for their control, but unfortunately this call doesn't pass the object (or even the type) of object for which the container is being constructed. If it had, then it would be so trivial to implement a treeview that generates custom tree nodes without loosing any of the nice templating, styling or anything else. Too bad.
With regard to multiple UI elements refering to the same model item:
The link referenced in my original post showed how simple it is to use the model and have information update between the UI and the model. In the example, the author was searching for text in a tree. Because he linked the 'IsSelected' property of the TreeViewItem to a property (say 'ItemIsSelected' for clarity) in the model, he could walk the tree, and successively select each matching node in the search by merely setting the 'ItemIsSelected' property. Nice. I agree.
Now, throw that model into two separate trees and oops, setting the 'ItemIsSelected' causes both trees to change their selected item. Inorder to prevent that, but have that sort of functionality in both trees, one has to implement two properties in the model: 'ItemIsSelected_TV1', and 'ItemIsSelected_TV2' and in the different TreeView's.
The alternative is to use GetContainerFromElement. This technique more cleanly separates the UI and model, though I have to agree with the example's elegance (minus the resulting attachment to the UI).
|
|
|
|
|
On some further reflection, I could add the command bindings dynamically in the TreeView and TreeViewItem function 'PrepareContainerForItemOverride(DependencyObject obj, object item).' I'd prefer to put it in the template though, so if you do know how to do that, that would be much nicer.
|
|
|
|
|
hb52134214 wrote: How does one attach a routed command handler in the template to get a node to respond to a SaveFile command.
You could have a button inside the HDT. Set its Command to SaveFile and the CommandParameter to the current DataContext. The CommandParameter set to the current DataContext tells code later on which File was clicked. Then, add the CommandBinding to the TreeView's CommandBindings (or CommandBindings higher up).
hb52134214 wrote: one has to implement two properties in the model: 'ItemIsSelected_TV1', and 'ItemIsSelected_TV2' and in the different TreeView's
You could always make ItemIsSelected an array or list of booleans. Then, in your binding, you can reference it in the trees like this:
{Binding ItemIsSelected[0]}
{Binding ItemIsSelected[1]}
That way, you can use any number of trees without having to modify the view model. And if you don't want to fill in ItemIsSelected with a certain number of items in advance (say, 2 items), you could always create a custom collection (probably derived from IList) that fills in values if they do not already exist and returns a default the first time they are accessed. So, the following binding would cause 3 items to be in the collection:
{Binding ItemIsSelected[2]}
|
|
|
|
|
The issue with the button is that the button essentially becomes the only way to trigger the Save command. You can't just catch the CanExecute and Execute events of the command. Consider a simple tree:
TV (tree view)
obj 1
obj 2
Say obj 1 is a project and obj 2 is a file. Obj 2 is selected, and the user hits the SaveProject or SaveFile menu item, or tool bar button, or key gesture. The event bubbles up the stack, but the stack really looks like:
TV
TVI_1
TVI_1_header (obj1)
TVI_1_items
TVI_2
TVI_2_header (obj2)
TVI_2_items
Before, when I said Obj 2 is selected, now it is interesting to consider... is TVI_2_header selected, or is TVI_2 selected? It could be the routed event would follow the path:
TVI_2_header -> TVI_2 -> TVI_1_items -> TVI_1 -> TV -> ...
Or,
TVI_2 -> TVI_1_items -> TVI_1 -> TV -> ...
Now consider the two events: SaveProject, and SaveFile. SaveProject never triggers (its not on the element chain). SaveFile only triggers when TVI_2_header is selected.
If anyone has any ideas for this, I'd love to hear them.
|
|
|
|
|
I think I'll write an article on this, because this TreeView issue really bugs me. I seem to always run it and it drives me nuts. Prior to now, I've just handled event in the TreeView, but its so much nicer for things to connect directly. Alas, I have a technique that works quite nicely.
To solve this problem, create an interface that enumerates command bindings. Say:
interface ICmdHandler {
IEnumerable<CommandBinding> CommandBindings { get; }
}
Next derive a class from TreeViewItem that determines if the header object is a command handler and then link the commands if it is.
class TVICommandForwarder : TreeViewItem {
protected override void OnHeaderChanged(object oldHeader, object newHeader) {
base.OnHeaderChanged(oldHeader, newHeader);
ICmdHandler oldCH = oldHeader as ICmdHandler;
ICmdHandler newCH = newHeader as ICmdHandler;
if (oldCH != null)
foreach (CommandBinding cb in oldCH.CommandBindings)
CommandBindings.Remove(cb);
if (newCH != null)
foreach (CommandBinding cb in newCH.CommandBindings)
CommandBindings.Add(cb);
}
protected override DependencyObject GetContainerForItemOverride() {
return new TVICommandForwarder();
}
}
Notice that if the header changes, it unlinks commands from the old one and links commands from the new header. Also notice that GetContainerForItemOverride() generates a new TVICommandForwarder class, which makes sure that any child nodes also support this mechanism.
Next, one needs to derive a class from TreeView so that it builds nodes using the TVICommandForwarder class.
class TVCommandForwarder : TreeView {
protected override DependencyObject GetContainerForItemOverride() {
return new TVICommandForwarder();
}
}
With that, now any object that gets loaded into the tree automatically gets its command handlers linked if the ICmdHandler interface is implemented. Now that is nice.
If anyone sees a fundamental error in my approach, please let me know. Otherwise, in the next few days I'll write up an article about this technique.
|
|
|
|
|
Dear Friends,
How to Do ZoomIn and Zoomout in Silverlight.Using Slider,
or Anyother Way Possible Suggest.
Thanks and Regards,
Munna
Muneer
|
|
|
|
|
|
Check this, http://forums.silverlight.net/forums/t/5941.aspx
April
Comm100 - Leading Live Chat Software Provider
modified 27-May-14 21:44pm.
|
|
|
|
|
Hello,
While creating a WPF application, I am facing issue with the WPF window style. As per requirement, I have to design the customize Control box (other than the default blue and the close/ min/ max/ restore button) but not able to do that.
Another way is by hide/ remove the ‘control box’ from the WPF window and add the head section on it. I can able to do that using the WPF window property WindowStyle = none.
But that hides the windows XP Taskbar too. Please suggest me that how can I implement that.
Regards
Manish Mukatimodified on Wednesday, February 17, 2010 12:50 AM
|
|
|
|
|
OKAY -- I've done a ton of searching and I've not found anything that can directly help me with what I need to do.
I'm building a business application with Silverlight 3.0 and I'm down to my VERY LAST issue!
I have 3 ComboBox controls (building, datacenter, locationType) where datacenter has a dependancy on building.
I have a listbox with customers (works great).
When a customer is selected, a list of existing locations is populated. (works great)
For these (and other) populated controls, I put out a message "loading...blah blah blah" then when the data comes back I display "data loaded...blah blah blah" which works great.
I've got my datacenter dependancy working great as well. Pick a building and the correct set of datacenters is loaded.
My editor fields are wrapped in a grid and a datasource for my location object was created. I populate it in my code by setting editorContainer.DataContext = selectedCustomerLocation; editorContainer is the grid that wraps all of my editor controls. Each control that edits the fields of the control is bound to the datacontext of the grid, but Expression does not seem to place the parent object name as a fully-qualified name. (example my xaml says 'BuildingNumber' instead of CustomerLocation.BuildingNumber) I expected the editor controls to self populate with the data once DataContext was set. (doesn't work)
I also want each combobox to display the correct selected item. I know I need to say SelectedText = "{Binding {fieldName} Converter=myValueConverter, ConverterParameter= ....". I also expected that in the Convert logic I perform the linq to lookup the building enumerated object and return the text value for the integer returned in the customer location object.
Not quite sure how to explicitely pass to the converter the value of the building ID (for example) AND the list of building name/values to have it return the correct text. So this is the first question which is the syntax to pass parameters into the converter?
The second question, then, is Expression put the datacontext in a self-contained location within the grid followed by my fields. Do I have to extend the <datacontext> end tag to wrap my editor controls? Or is DataContext all wrong? I think I saw one guy use Data Template???
Any help at all would be great. I found a ton of examples but all of them are either too simplistic (working with internal collections statically created and never dealing with an externally selected object to bind with) or buried within the context of a DataGrid which I'm not using.
modified on Monday, January 4, 2010 4:53 PM
|
|
|
|
|
Michael Eber wrote: SelectedText = "{Binding {fieldName} Converter=myValueConverter, ConverterParameter=
A trick I used to pass multple values (my entire collection) to the converter was to not bind to the field name. Try this - SelectedText = "{Binding Converter=myValueConverter, ConverterParameter= ....".
Michael Eber wrote: The second question, then, is Expression put the datacontext in a self-contained location within the grid followed by my fields. Do I have to extend the end tag to wrap my editor controls? Or is DataContext all wrong? I think I saw one guy use Data Template???
Not quite sure I understand your question. However, IMO, the datacontext looks fine. DataTemplates are used to specify the visual structure of a data object, so DataContexts and DataTemplates are actually two different things (used differently).
There are only 10 types of people in this world — those who understand binary, and those who don't. |
|
|
|
|
|
What I mean as far as passing data to the converter is this:
I need to pass the collection of objects that populate the dropdown box.
I also need to pass the value set by the object.
I've seen a setup like this for passing a date: ConvertParameter=0\d\ But I have no idea what that means or how they are actually passing the date field.
|
|
|
|
|
Michael Eber wrote: ConvertParameter=0\d
AFAIK, ConvertParameter does not support binding. So, you can only pass 'hardcoded' values like 0\d, which will format your date. The date is passed using the binding value e.g. Text="{Binding myDate, Converter={StaticResource myConverter}}" .
However, the entire collection can be passed into the converter. The appropriate value(s) can then be used inside the converter to construct the value you want to display in your UI.
There are only 10 types of people in this world — those who understand binary, and those who don't. |
|
|
|
|
|