|
|
I'v been struggling with this all morning. Cold use some help...
I have created a HierarchicalDataTemplate used in a treeview. I have a class that overrides the basic treeview, called TreeViewEx, that I got from someone here at CP. (Can't remember who - sorry).
At any rate, here's the template:
<HierarchicalDataTemplate DataType="{x:Type models:TreeItemModel}"
ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal"
Margin="2">
<TextBlock Margin="0,0,5,0">
<Hyperlink NavigateUri="{Binding Caption}"
Foreground="#0C2DAA"
Style="{StaticResource linkStyle}"
Command="{Binding Path=DataContext.SelectedLinkCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={ x:Type views:MainWindowView}}}">
<InlineUIContainer>
<TextBlock Text="{Binding Caption}" />
</InlineUIContainer>
</Hyperlink>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
and here's the treeview usage:
<controls:TreeViewEx ItemsSource="{Binding TreeItems}"/>
and finally the code behind:
private TreeItemModel _SelectedTreeLink;
public TreeItemModel SelectedTreeLink
{
get { return _SelectedTreeLink; }
set
{
if (_SelectedTreeLink != value)
{
_SelectedTreeLink = value;
RaisePropertyChanged("SelectedTreeLink");
}
}
}
private ICommand _SelectedLinkCommand;
public ICommand SelectedLinkCommand
{
get
{
if (_SelectedLinkCommand == null)
_SelectedLinkCommand = new RelayCommand(SelectedLinkExecuted, SelectedLinkCanExecute);
return _SelectedLinkCommand;
}
}
private bool SelectedLinkCanExecute()
{
return true;
}
private void SelectedLinkExecuted()
{
var item = SelectedTreeLink;
}
The tree loads with data fine, and they tree nodes are hyperlinks. The command works. However, when I break in the SelectedLinkExecuted method, SelectedTreeLink = null.
How can I get a reference to the data model behind the link?
Thanks
If it's not broken, fix it until it is
|
|
|
|
|
You could try using the CommandParameter property of the Hyperlink. Just set it to "{Binding}" , and the RoutedEventArgs class should contain a reference to the current object in its Parameter property.
You'll have to use the RelayCommand a bit differently, though, to get that RoutedEventArgs object into your handler.
|
|
|
|
|
Ok, I think I understand. Do you have an example?
If it's not broken, fix it until it is
|
|
|
|
|
I'm not that familiar with whichever RelayCommand implementation you're using (I've developed my own ways of organizing command-bindings), but the XAML is just:
CommandParameter="{Binding}"
But if it was a normal RoutedEventHandler, it would look something like:
private void ExecuteMyCommand(object sender, ExecutedRoutedEventArgs e)
{
MyModelClass modelItem = (MyModelClass)(e.Parameter);
}
|
|
|
|
|
I think you got that from me lol. From the code you posted, it seems like you didn't bind the treeview's selected item to the VM.
|
|
|
|
|
Actually I had this earlier...
<controls:TreeViewEx ItemsSource="{Binding TreeItems}"
SelectedItemEx="{Binding SelectedTreeLink}"/>
but it didn't change anything so I removed it.
and here's the TreeViewEx. Is this your code?
public class TreeViewEx : TreeView
{
public static readonly DependencyProperty SelectedItemExProperty = DependencyProperty.Register("SelectedItemEx", typeof(object),
typeof(TreeViewEx), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnSelectedItemExChanged)));
public TreeViewEx()
: base()
{
SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(TreeViewEx_SelectedItemChanged);
}
public object SelectedItemEx
{
get { return (object)GetValue(SelectedItemExProperty); }
set { SetValue(SelectedItemExProperty, value); }
}
public bool SelectItem(object item)
{
if ((SelectedItem != null) && (SelectedItem.Equals(item)))
return true;
if (!ExpandAndSelectItem(this, item))
{
UpdateLayout();
return ExpandAndSelectItem(this, item);
}
return true;
}
private static void OnSelectedItemExChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TreeViewEx treeView = d as TreeViewEx;
if ((object)treeView != null)
treeView.SelectItem(e.NewValue);
}
private void TreeViewEx_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
SetValue(SelectedItemExProperty, e.NewValue);
}
private bool ExpandAndSelectItem(ItemsControl parentContainer, object itemToSelect)
{
TreeViewItem tvi = parentContainer.ItemContainerGenerator.ContainerFromItem(itemToSelect) as TreeViewItem;
if ((object)tvi != null)
{
tvi.IsSelected = true;
tvi.BringIntoView();
tvi.Focus();
return true;
}
for (int nIndex = 0; nIndex < parentContainer.Items.Count; nIndex++)
{
TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromIndex(nIndex) as TreeViewItem;
if (((object)currentContainer != null) && (currentContainer.Items.Count > 0))
{
bool bIsExpanded = currentContainer.IsExpanded;
if (currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
EventHandler handler = null;
handler = new EventHandler(delegate
{
if (currentContainer.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
if (ExpandAndSelectItem(currentContainer, itemToSelect) == false)
currentContainer.IsExpanded = false;
currentContainer.ItemContainerGenerator.StatusChanged -= handler;
}
});
currentContainer.ItemContainerGenerator.StatusChanged += handler;
if (currentContainer.IsExpanded == false)
currentContainer.IsExpanded = true;
}
else
{
if (ExpandAndSelectItem(currentContainer, itemToSelect) == false)
currentContainer.IsExpanded = bIsExpanded;
else
return true;
}
}
}
return false;
}
}
If it's not broken, fix it until it is
|
|
|
|
|
You need to make SelectedItemEx mode=TwoWay.
|
|
|
|
|
Still no joy.
Tomorrow I'm going to abstract this out into a sandbox app. That'll allow me to work without all the other app code I have. Plus if I need to I can post the sample app for others to see.
I'll post again after that.
Thanks
If it's not broken, fix it until it is
|
|
|
|
|
I just tested it in my test app and SelectedItemEx is working as expected. Supports 2-way binding just as it should. My TreeViewEx XAML is:
<fx:TreeViewEx Name="treeView" Grid.Row="0" Margin="20,20,20,20" SelectedItemEx="{Binding o, Mode=TwoWay}">
.
.
</fx:TreeViewEx>
and o is just an object that lives in the VM.
Some things you might want to look at:
1) make sure the DataContext is correct and you are binding to the right type of object.
2) make sure you are not setting TreeViewEx.SelectedItemEx manually... this is not an issue with my control, this is a general WPF "issue". If you have a binding set and then set the value directly, it clears the binding.
|
|
|
|
|
Oh wait...you know whats probably happening?
You click on the hyperlink, so that executes the hyperlink command, but its not selecting the TreeViewItem because the hyperlink ate it.
either:
1) make the hyperlink select the node
2) make the hyperlink pass in node in the CommandParamter
|
|
|
|
|
Ok, I see.
SledgeHammer01 wrote: make the hyperlink select the node
I had code somewhere to do this. Lemme look for it.
SledgeHammer01 wrote: make the hyperlink pass in node in the CommandParamter
Interesting idea. Can you share how to do this?
Thanks
If it's not broken, fix it until it is
|
|
|
|
|
Ok, I created a sandbox app[^] and I figured out what's happening.
See this[^]
Then this[^]
The node has to be clicked first.
Yesterday you suggested making the hyperlink select the node. This seems like a decent approach, although I could use some help with it.
Many thanks
If it's not broken, fix it until it is
|
|
|
|
|
Probably in the hyperlink xaml, add a CommandParamter="{Binding}". You'll need to use RelayCommand<T> instead of RelayCommand though. RelayCommand<RoutedEventArgs> I suppose. That should contain the VM for the node.
|
|
|
|
|
That did it!
_SelectedLinkCommand = new RelayCommand<NodeModel>(p => selectedTreeItemExecuted(p), p => selectedTreeItemCanExecute(p));
Worked like a charm. Thanks
ya know, I notice that there's not alot on CP about a TreeView with Hyperlinks. Maybe I'll turn this into an article. WHat do ya think?
If it's not broken, fix it until it is
|
|
|
|
|
What's the difference?
I can't seems to find a difinitive description.
If it's not broken, fix it until it is
|
|
|
|
|
Don't know if there is a definitive description but the Application template is the most basic and provides limited boilerplate code to get started. The Business Application template throws in a whole lot of extra boilerplate providing functionality that a typical LOB application.
Quote from Walkthrough: Using the Silverlight Business Application Template[^]
"The Silverlight Business Application template creates a project that automatically includes many features that you typically want in a business application, such as controls to log in users and to register new users. The project created by the template is also set up to use Silverlight navigation, which means you can easily add new Silverlight pages for additional functionality."
"You get that on the big jobs."
|
|
|
|
|
|
I have had an odd request and just cant find any info on how (or even if) this can be done.
I am using the wpf toolkit charting controls to create a column chart. This bit is simple enough but what about showing columns in columns. The data i am using is like this:
Group
- GroupName
- Strength
- GroupItems()
GroupItem
- ItemName
- ItemStrength
So i have an observble collection of 'Groups' which is the itemsource for my column chart using the groupname as the independent and the Strength as the dependent. This works fine.
What you can see though is that each group also has a collection of 'GroupItems' (there can be 2 to 5 of these per group). What i would like is that within the outline of the 'Group' column i can see the columns for the 'GroupItems' that belong to that group.
Is this even possible?
|
|
|
|
|
Working on a WPF MVVM app...
I have this button:
<Button Background="Transparent"
BorderBrush="Transparent"
Command="{Binding RunCommand}"
Margin="2"
Width="120"
Height="40">
<StackPanel Orientation="Horizontal">
<Image Source="/DemoUI;component/Images/run.png"
Height="32"
Width="32"
Margin="3"/>
<TextBlock Text="Run Test"
VerticalAlignment="Center"/>
</StackPanel>
</Button>
It's bound to RunCommand:
private ICommand _RunCommand;
public ICommand RunCommand
{
get
{
if (_RunCommand == null)
{
_RunCommand = new RelayCommand(runTest, runTestCanExecute);
}
return _RunCommand;
}
}
Here's the methods:
private void runTest()
{
isTesting = true;
var device = (from d in tempDevices
where d.DeviceId == SelectedDevice.DeviceId
select d).FirstOrDefault();
diag.ExecuteTest(device, SelectedTest);
}
private bool runTestCanExecute()
{
return !isTesting;
}
isTesting is defaulted to False. The button does not disable.
Anyone see what's wrong?
If it's not broken, fix it until it is
modified 11-Jul-12 14:53pm.
|
|
|
|
|
Are you sure its not disabled? You are overriding the content, so it may appear to be enabled, but really be not clickable because you aren't handling the disabled state.
|
|
|
|
|
Ya, it's still responds to clicks.
If it's not broken, fix it until it is
|
|
|
|
|
Copy & pasted your code into a test project and it disables just fine. Make sure your DataContext is set properly or it won't know where the command is. I.e. put a break point on your CanExecute handler and make sure its actually getting called.
|
|
|
|
|
Ok, I see what's going on. isTesting is somehow reverting to false by the time the CanExecute is called.
Stay tuned.....
If it's not broken, fix it until it is
|
|
|
|
|
Try this, change you button to use a Click="" event instead of a Command.
Never underestimate the power of human stupidity
RAH
|
|
|
|