|
Pete O'Hanlon wrote: Pete Brown, at Microsoft, promised me this would be in
There's your mistake - never trust anyone called "Pete"!
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
There's an excellent article here[^] on how to mitigate the airspace issue.
|
|
|
|
|
I have several datatemplates defined in a resource dictionary (in Resources project)
My application is in a separate project that references the Resources project. Appropriate datatempalte is loaded depending upon the object that is set to the datacontext during run time. Some of these datatemplates have buttons. How do I handle the click event of these buttons. I tried eventsetter but got XAML errors abt not being able to set eventhandler.
Thanks
|
|
|
|
|
Templates work only with triggers. These triggers are use to set various properties of the button.
See this for a more detailed understanding:Triggers in WPF[^]
ClickEvent is handled in code. It is completely independent from Triggers. You should be able to attach an event handler in the XAML declaration like this.
<Button x:Name="button1" Click="button1_onClick" Template={StaticResource MyTemplate} />
In code:
private void button1_onClick(object sender, RoutedEventArgs e)
{
}
See MSDN for more details:
Button Class[^]
ButtonBase.Click Event[^]
Beauty cannot be defined by abscissas and ordinates; neither are circles and ellipses created by their geometrical formulas.
Source
|
|
|
|
|
Thanks Amitosh S.M. for your reply.
I ended up binding a command to the button in datatemplate. And then defining this command as a property in my class. Worked pretty well.
<Button Name="btnControl" Canvas.ZIndex="10" Canvas.Left="175" Canvas.Top="25" Content="ICOM"
MaxWidth="75" MaxHeight="75" Style="{StaticResource ICOMButtonStyle}" Command="{Binding LaunchControlModule}"/>
Public ReadOnly Property LaunchControlModule() As ICommand
Get
Return New ViewModelCommand(AddressOf HandlerLaundControlModule)
End Get
End Property
Private Sub HandlerLaundControlModule()
ShowControlModule = True
End Sub
Public Class ViewModelCommand
Implements ICommand
Private _handler As Action
Public Sub New(ByVal handler As Action)
_handler = handler
End Sub
#Region "ICommand Members"
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
Return True
End Function
Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) Implements System.Windows.Input.ICommand.CanExecuteChanged
Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute
_handler()
End Sub
#End Region
|
|
|
|
|
I'd like to get your thoughts on this design...
So I am writing some software for personal use. I'm an independent developer, and I wanted to put together an app to track the work I'm doing and handle invoicing and billing. Conceptually I understand what I want, but programmatically I am working on the design. This is more about me learning than producing an app.
So, I am building this WPF/MVVM app in components. Most pieces are user controls. I decided that I would use relay messages to communicate between the user control a& its host.
For example, the Client View is found in the Client Center.
Each Client is viewed in its own tab in the Client Center. The Client Center has a toolbar with New, Edit, Save, Cancel and Print buttons. Since the center doesn't really know about the Client views in the tabs, the Center will broadcast a message for each function. The Client Center has an instance of a TabManager class, which is a collection of TabItems. The TabItem's Tag property contains the PK of the entity in the tab's content. So, this is available in the View Model.
As an example, when the Save button on the Client Center toolbar is clicked, then it does:
ClientViewSaveMessage message = new ClientViewSaveMessage
{
Clientid = getActiveTabEntityPK()
};
Messenger.Instance.Send<ClientViewSaveMessage>(message);
Then in the client view I have
public ClientViewModel
{
messenger.Instance.Register<ClientViewSaveMessage>(p => receiveSaveMessage(ClientViewSaveMessage)p)
}
and then the receiveSaveMessage
private void receiveSaveMessage(ClientViewSaveMessage message)
{
if(this.Client.Id == message.ClientId)
{
saveChanges();
}
}
This technique has the advantage of decoupling all the components. So far it's working well. It's easy to develop, and so far, easy to maintain.
A drawback might be that I will need a lot of messages. I could make one message that has different properties...
Any way, I'd like to hear what you guys think.
Thanks
If it's not broken, fix it until it is
|
|
|
|
|
2 things come to mind. You have low expectations of your client base (reasonable in an independant developer) as you are using a tabcontrol as your client collection UI. I would use a grid. In the late 90 after about 5 years of consulting I had well over 50 organisations I was keeping tabs on, once a client or even a quote always a potential.
One reason not to use a messaging paradigm is that it is a bitch to maintain, there is no direct link between the firer of the message and the consumer of that message. a bitch to support.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Mycroft Holmes wrote: You have low expectations of your client base (reasonable in an independant
developer) as you are using a tabcontrol as your client collection UI
I never said that. I said when a client is opened, it's opened in a new tab in the client center. There is a list of clients on the side .
Mycroft Holmes wrote: One reason not to use a messaging paradigm is that it is a bitch to maintain,
there is no direct link between the firer of the message and the consumer of
that message. a bitch to support.
That's what I said in my OP. Other than that, I cannot see any reason not to use this design,
If it's not broken, fix it until it is
|
|
|
|
|
Kevin Marois wrote: There is a list of clients on the side
Ah somewhat like the outlook bar, that makes sense.
Kevin Marois wrote: Other than that,
Yeah but support is a major factor in the cost of an app and we will go a long way to make it easier for the next poor sod who has to support the app.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
SO far it seems to be working well. Overall it's very little code, but the number of message classes is growing. I think that's where the confusion might come in. I could make one generic message class.
As far as knowing who sent a message, the call stack shows that. But I understand what you're saying.
If it's not broken, fix it until it is
|
|
|
|
|
Kevin
Can you describe in one paragraph how you constructed your dynamic tab control. I have a similar requirement (max 5 tabs). If so please email me direct (your email is disabled on the messages).
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
I'm not sure what you mean by "constructed your dynamic tab control.
If it's not broken, fix it until it is
|
|
|
|
|
I was under the impression your tabcontrol populated the tabs and content based on data rather than at design time. I have a number of ideas how to achieve this but I am trying to reduce the experimentation time required to get it working and was hoping you had already researched this and had some guidance or links to examples you had used.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Ok, I see
My tab control is bound to a class, so in the XAML I have
<TabControl Grid.Row="2"
Grid.Column="2"
x:Name="MyTabControl"
BorderBrush="#6593CF"
Background="White"
ItemsSource="{Binding TabManager.Tabs}"
SelectedItem="{Binding TabManager.ActiveTab}"
Margin="2"/>
My Tab Manager class is this
public class ViewManager : BindableBase, ITabManager
{
#region Events
public event EventHandler TabActivated;
#endregion
#region Bound Properties
private ObservableCollection<TabItem> _Tabs = new ObservableCollection<TabItem>();
public ObservableCollection<TabItem> Tabs
{
get { return _Tabs; }
set
{
if (_Tabs != value)
{
_Tabs = value;
RaisePropertyChanged("Tabs");
}
}
}
private TabItem _ActiveTab;
public TabItem ActiveTab
{
get { return _ActiveTab; }
set
{
if (_ActiveTab != value)
{
_ActiveTab = value;
RaisePropertyChanged("ActiveTab");
if (TabActivated != null)
{
TabActivated(this, new EventArgs());
}
}
}
}
private bool _TabsVisible;
public bool TabsVisible
{
get { return _TabsVisible; }
set
{
if (_TabsVisible != value)
{
_TabsVisible = value;
RaisePropertyChanged("TabsVisible");
}
}
}
#endregion
#region Public Methods
public void ActivateTab(ViewInfo TabInfo)
{
var tab = GetTab(TabInfo);
ActiveTab = tab;
}
public void AddTab(TabItem Tab, bool Activate = true, int IndexPosition = -1)
{
if (IndexPosition == -1)
{
Tabs.Add(Tab);
}
else
{
Tabs.Insert(IndexPosition, Tab);
}
if (Activate)
{
ActivateTab((ViewInfo)Tab.Tag);
TabsVisible = Tabs.Count > 0;
}
}
public bool CloseAllButThisTab(ViewInfo TabInfo)
{
bool retVal = true;
for (int i = Tabs.Count - 1; i >=0; i-- )
{
var tab = Tabs[i];
var tabInfo = (ViewInfo)tab.Tag;
if (tabInfo.View == TabInfo.View && tabInfo.EntityId == TabInfo.EntityId)
{
continue;
}
else
{
retVal = CloseTab(tabInfo);
if (!retVal)
{
break;
}
}
}
return retVal;
}
public bool CloseAllTabs()
{
bool retVal = true;
for(int i = Tabs.Count - 1; i > 0; i--)
{
var tab = Tabs[i];
retVal = CloseTab((ViewInfo)tab.Tag);
}
return retVal;
}
public bool CloseTabAtIndex(int Index)
{
Tabs.RemoveAt(Index);
return true;
}
public bool CloseActiveTab()
{
return CloseTab((ViewInfo)ActiveTab.Tag);
}
public bool CloseTab(ViewInfo TabInfo)
{
bool retVal = true;
var tab = GetTab(TabInfo);
if (tab != null)
{
ActivateTab((ViewInfo)tab.Tag);
var view = (UserControl)tab.Content;
if (view.DataContext is _CenterViewModelBase)
{
_CenterViewModelBase centerVM = (_CenterViewModelBase)view.DataContext;
for(int i = centerVM.TabManager.Tabs.Count - 1; i > -1 ; i--)
{
TabItem centerTab = centerVM.TabManager.Tabs[i];
bool closed = centerVM.TabManager.CloseTab((ViewInfo)centerTab.Tag);
if (!closed)
{
retVal = false;
break;
}
}
}
else
{
dynamic vm = (_DataEntryViewModelBase)view.DataContext;
retVal = vm.PromptSaveChanges(true);
}
if (retVal)
{
Tabs.Remove(tab);
TabsVisible = Tabs.Count > 0;
}
}
return retVal;
}
public bool HasDirtyTabs()
{
bool retVal = false;
foreach(var tab in Tabs)
{
ActivateTab((ViewInfo)tab.Tag);
var view = (UserControl)tab.Content;
_DataEntryViewModelBase vm = (_DataEntryViewModelBase)view.DataContext;
retVal = vm.IsDirty();
if (retVal)
{
break;
}
}
return retVal;
}
public bool IsTabOpen(ViewInfo TabInfo)
{
TabItem tabItem = GetTab(TabInfo);
return tabItem != null;
}
public TabItem GetTab(ViewInfo TabInfo)
{
TabItem retVal = null;
foreach (var tab in Tabs)
{
ViewInfo vi = (ViewInfo)tab.Tag;
if (vi.View == TabInfo.View && vi.EntityId == TabInfo.EntityId)
{
retVal = tab;
break;
}
}
return retVal;
}
public void RefreshTabInfo(AppMessageArgs Args)
{
foreach (var tab in Tabs)
{
UserControl view = tab.Content as UserControl;
_DataEntryViewModelBase vm = view.DataContext as _DataEntryViewModelBase;
ViewInfo vi = (ViewInfo)tab.Tag;
if (vi.View == Args.ViewType)
{
tab.Header = vm.GetEntityCaption(); ;
vi.EntityId = vm.GetEntityId();
tab.Tag = vi;
}
}
}
public bool SaveAllOpenTabs()
{
bool retVal = false;
foreach (var tab in Tabs)
{
ActivateTab((ViewInfo)tab.Tag);
var view = (UserControl)tab.Content;
_DataEntryViewModelBase vm = (_DataEntryViewModelBase)view.DataContext;
retVal = vm.PromptSaveChanges(true);
if (!retVal)
{
break;
}
}
return retVal;
}
#endregion
}
Then when I want to add a tab from a VM I do this:
private void loadView(BOMArgs Args)
{
ViewInfo tabInfo = new ViewInfo
{
View = View.BOM,
EntityId = Args.BOM.Id
};
if (tabInfo.EntityId > 0 && TabManager.IsTabOpen(tabInfo))
{
TabManager.ActivateTab(tabInfo);
}
else
{
BOMViewModel vm = new BOMViewModel(Engine);
vm.Load(Args);
UserControl view = new BOMView();
view.DataContext = vm;
TabItem tabItem = new TabItem
{
Header = vm.GetEntityCaption(),
Content = view,
Tag = tabInfo
};
TabManager.AddTab(tabItem, true);
}
}
Works perfect, although I need to de-couple the view => vm dependency in the 'else' section.
If it's not broken, fix it until it is
|
|
|
|
|
Excellent, thanks for the info, it clarified my design immensely. Have a bunch of 5s
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Ok, what am I doing wrong here???
I have a Client Entity:
public class ClientEntity : _EntityBase
{
private string _ClientName = string.Empty;
public string ClientName
{
get { return _ClientName; }
set
{
if (_ClientName != value)
{
_ClientName = value;
RaisePropertyChanged("ClientName");
IsDirty = true;
}
}
}
private PayTypeEntity _PayType;
public PayTypeEntity PayType
{
get { return _PayType; }
set
{
if (_PayType != value)
{
_PayType = value;
RaisePropertyChanged("PayType");
IsDirty = true;
}
}
}
}
In my XAML, I am binding the PayTypes combo like this:
<ComboBox Grid.Row="1"
Grid.Column="3"
ItemsSource="{Binding PayTypes}"
SelectedValue="{Binding Client.PayType.Id, Mode=TwoWay}"
SelectedValuePath="Id"
DisplayMemberPath="Caption"/>
When I select a PayType it does not get set on the ClientEntity.
If it's not broken, fix it until it is
|
|
|
|
|
You're not binding to the PayType property on the ClientEntity class; you're binding to the Id property on the PayTypeEntityClass instance returned from the PayType property on the ClientEntity class.
Try this:
<ComboBox
Grid.Row="1" Grid.Column="3"
ItemsSource="{Binding PayTypes}"
SelectedValue="{Binding Client.PayType}"
DisplayMemberPath="Caption"
/>
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
That worked. Thank you
If it's not broken, fix it until it is
|
|
|
|
|
Ok, so I'm still stuck here. I thought this was working, but it's not quite right. It saved the Id ok, but when I reopen it, it doesn't set the value back to what I selected. Here's what I have:
<ComboBox Grid.Row="1"
Grid.Column="3"
IsEnabled="{Binding Path=FieldEnableHelper.PayType}"
ItemsSource="{Binding PayTypes}"
SelectedValue="{Binding Client.PayType}"
DisplayMemberPath="Caption"
TabIndex="10"
Style="{StaticResource ComboBoxStyle}"
Width="150"/>
If it's not broken, fix it until it is
|
|
|
|
|
hi i am trying to create an navigation bar which allow me to select the page based on the dropdownlist when i select the title
e.g
home->pg1->pg2
and when pg1 is clicked a dropdownbox would dropdown and show the choices which are childs of home.
is there a name or a control which have this already?
please advice
|
|
|
|
|
Menu
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
I want to make a Choose from list in my silverlight application. like click one button it will show a list of suggestion from that you can choose.
|
|
|
|
|
Its called a combo box and it binds to a collection of objects (your class of suggestions) and a selected object, the suggestion the user has selected
Add a button on the view that populates the ObservableCollection<suggestionobject> in the Viewmodel/code behind.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Sorry but Combo box shows only one dimensional data but I have to show multiple column from that they can choose.
|
|
|
|
|
Actually a combobox can take a DataTemplate and display multiple columns.
You use a ListBox or a DataGrid, it really depends on your requirements.
Never underestimate the power of human stupidity
RAH
|
|
|
|