Wanderer
Topic Author
Posts: 168
Joined: 08 May 2017, 18:36

(C++) How to send data between two views?

16 Jun 2017, 18:06

I have listBox, and all item have button. Each button is same, that means, if I click on button, new view is appear, where is TextBox. This textBox should change item name which affect name clicked button. How Can I do this?
I looked in to UserControl tutorial for inspiration, where is control color with buttons. But code not working because buttons are not tied with color.
 
Wanderer
Topic Author
Posts: 168
Joined: 08 May 2017, 18:36

Re: (C++) How to send data between two views?

17 Jun 2017, 10:55

I resolved it myself. When button is clicked, it send this to the another class where I manage buttons from main grid. And send text to the new view, throught create pointer to
the xamlView->FindName<Element>("textBox");
When button OK (in new view & window) is clicked, it take text from TextBox and call class button which send this and change text. And I forgot to mention, the button is custom user control. It is good idea? Or are there better solutions?
 
User avatar
sfernandez
Site Admin
Posts: 2984
Joined: 22 Dec 2011, 19:20

Re: (C++) How to send data between two views?

19 Jun 2017, 20:41

If I understood correctly what you are trying to do, the following view model and xaml can be useful.

This sample shows a toggle Button for each item to enable editing the item name. When pressed a TextBox with two buttons (Ok, Cancel) is shown, if Ok button is pressed then item's name is changed, if cancel button is pressed current name is restored.

Helper class that exposes a command that executes a delegate action:
struct DelegateCommand: public BaseCommand
{
    typedef Delegate<void (BaseComponent*)> CommandAction;
    CommandAction action;

    DelegateCommand(const CommandAction& action_): action(action_) { }

    NsBool CanExecute(BaseComponent* ) const override { return true; }
    void Execute(BaseComponent* param) const override { action(param); }

    NS_IMPLEMENT_INLINE_REFLECTION_(DelegateCommand, BaseCommand)
};
View model for each item:
NS_DECLARE_SYMBOL(Name)
NS_DECLARE_SYMBOL(IsEditingName)
NS_DECLARE_SYMBOL(EditedName)
NS_DECLARE_SYMBOL(AcceptName)
NS_DECLARE_SYMBOL(DiscardName)

struct DataItem: public BaseComponent, public INotifyPropertyChanged
{
    DataItem(const NsChar* n): name(n), isEditingName(false), editedName(n)
    {
        acceptName = *new DelegateCommand(MakeDelegate(this, &DataItem::OnAcceptName));
        discardName = *new DelegateCommand(MakeDelegate(this, &DataItem::OnDiscardName));
    }

    PropertyChangedEventHandler& PropertyChanged() { return changed; }

    const NsChar* GetName() const { return name.c_str(); }

    NsBool GetIsEditingName() const { return isEditingName; }
    void SetIsEditingName(NsBool value) { if (isEditingName != value) { isEditingName = value; Changed(NSS(IsEditingName)); } }

    const NsChar* GetEditedName() const { return editedName.c_str(); }
    void SetEditedName(const NsChar* value) { if (editedName != value) { editedName = value; Changed(NSS(Name)); } }

    ICommand* GetAcceptName() const { return acceptName.GetPtr(); }

    ICommand* GetDiscardName() const { return discardName.GetPtr(); }

    NS_IMPLEMENT_INTERFACE_FIXUP

private:
    void Changed(NsSymbol prop) { changed(this, PropertyChangedEventArgs(prop)); }

    void OnAcceptName(BaseComponent*)
    {
        name = editedName; Changed(NSS(Name));
        SetIsEditingName(false);
    }

    void OnDiscardName(BaseComponent*)
    {
        editedName = name; Changed(NSS(EditedName));
        SetIsEditingName(false);
    }

private:
    PropertyChangedEventHandler changed;
    NsString name;
    NsString editedName;
    NsBool isEditingName;
    Ptr<DelegateCommand> acceptName;
    Ptr<DelegateCommand> discardName;

    NS_IMPLEMENT_INLINE_REFLECTION(DataItem, BaseComponent)
    {
        NsImpl<INotifyPropertyChanged>();

        NsProp("Name", &DataItem::GetName);
        NsProp("IsEditingName", &DataItem::GetIsEditingName, &DataItem::SetIsEditingName);
        NsProp("EditedName", &DataItem::GetEditedName, &DataItem::SetEditedName);
        NsProp("AcceptName", &DataItem::GetAcceptName);
        NsProp("DiscardName", &DataItem::GetDiscardName);
    }
};
Code-behind class for this sample:
struct ListBoxEditItem: public Grid
{
    TestGrid()
    {
        InitializeComponent();

        // test data!!!
        Ptr<Collection> items = *new Collection();
        Ptr<DataItem> item = *new DataItem("John");
        items->Add(item.GetPtr());
        item = *new DataItem("Mike");
        items->Add(item.GetPtr());
        item = *new DataItem("Luke");
        items->Add(item.GetPtr());

        SetDataContext(items.GetPtr());
    }

private:
    void InitializeComponent()
    {
        GUI::LoadComponent(this, "ListBoxEditItem.xaml");
    }

    NS_IMPLEMENT_INLINE_REFLECTION(ListBoxEditItem, Grid)
    {
        NsMeta<TypeId>("ListBoxEditItem");
    }
};

void RegisterComponents() const
{
    //...
    NsRegisterComponent<ListBoxEditItem>();
}
Associated Xaml for the ListBoxEditItem class:
<Grid x:Class="ListBoxEditItem"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid.Resources>
        <DataTemplate x:Key="itemTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="30"/>
                    <ColumnDefinition Width="50"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding Name}" VerticalAlignment="Center" Margin="3,0,4,0"/>
                <ToggleButton Grid.Column="1" Grid.ColumnSpan="2" Content="Edit Name" IsChecked="{Binding IsEditingName}"/>
                <Rectangle x:Name="editBg" Grid.ColumnSpan="3" Fill="Pink" Visibility="Hidden" Margin="-4"/>
                <TextBox x:Name="editName" Text="{Binding EditedName}" Margin="0,0,4,0" Visibility="Hidden"/>
                <Button x:Name="acceptName" Grid.Column="1" Content="OK" Command="{Binding AcceptName}" Visibility="Hidden"/>
                <Button x:Name="discardName" Grid.Column="2" Content="Cancel" Command="{Binding DiscardName}" Visibility="Hidden"/>
            </Grid>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding IsEditingName}" Value="True">
                    <Setter TargetName="editBg" Property="Visibility" Value="Visible"/>
                    <Setter TargetName="editName" Property="Visibility" Value="Visible"/>
                    <Setter TargetName="acceptName" Property="Visibility" Value="Visible"/>
                    <Setter TargetName="discardName" Property="Visibility" Value="Visible"/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </Grid.Resources>
    <ListBox ItemsSource="{Binding}" ItemTemplate="{StaticResource itemTemplate}"
        Width="300" Height="200"/>
</Grid>
 
Wanderer
Topic Author
Posts: 168
Joined: 08 May 2017, 18:36

Re: (C++) How to send data between two views?

21 Jun 2017, 14:02

Thanks,
1. but my orignal question was, how to send data between views, that means from this list box with Main Grid, and new view in new window. This new window and view will appear when button is clicked inside ListBox (new view will be in new window like when you click on options in some desktop program). And in new window with view will be TextBox with buttons Ok to accept and change name of selected button inside ListBox.

2. In your code example, how ListBox bind data when you have only ItemsSource="{Binding}" ? I copy only some part of code like NS_IMPLEMENT_INTERFACE_FIXUP, in to my existing code, because I do ListBox different. (with observable collection and with user control as item).
 
User avatar
sfernandez
Site Admin
Posts: 2984
Joined: 22 Dec 2011, 19:20

Re: (C++) How to send data between two views?

22 Jun 2017, 16:35

1. When an item Edit button is clicked, you will open or access the edit window and set there the item as DataContext. And that edit window will modify the same data item, and when edition is finished the data will be updated and the new info will appear in the initial ListBox.

2. The "{Binding}" value uses DataContext as source of the binding, and also as returned value of the binding. So when I set in my ListBoxEditItem class the 'items' collection as DataContext, that value is also set as ItemsSource in the ListBox.
 
Wanderer
Topic Author
Posts: 168
Joined: 08 May 2017, 18:36

Re: (C++) How to send data between two views?

24 Jun 2017, 15:07

Thanks, but I have few questions:

1. You mean in codebehind? Like this:
// code from button inside ContainerItem
void ContainerItem::ContainerItemButton_Click(BaseComponent* sender, const RoutedEventArgs& e)
{
	//  ... code
	MyGrid * mg = xamlElementChooseWin->FindName<MyGrid>("ChoiceGrid");
	mg->SetDataContext(this);
	//  ... code
}
?
This is working for me, but I tried it with only xaml, but without succes. But maybe because I use UserControl instead BaseComponent?

2. What is advantage when delegates are used? Because, with my previous code, it is less work = inside Ok button, I get text from TextBox and set it back to item (in c++).
 
User avatar
sfernandez
Site Admin
Posts: 2984
Joined: 22 Dec 2011, 19:20

Re: (C++) How to send data between two views?

26 Jun 2017, 19:07

1. If you want to work with pure MVVM, you should try to avoid referencing UI elements, but work with view model data instead. What I meant was something like:
void ItemViewModel::OnEditItemCommand(BaseComponent* param)
{
    ItemEditorViewModel* itemEditorVM = GetItemEditor(); // somehow you should be able to access parent view model
    itemEditorVM->SetEditedItem(this); // this will modify EditedItem property in the parent view model
}
And the view the manages the edit of the item will have a binding to that property:
...
<Grid DataContext="{Binding EditedItem}">
  ...
</Grid>
2. It is just a different approach. MVVM tries to avoid referencing UI elements because that way, you can modify the xaml with a new design and the application can continue working. With your approach you need to know that a TextBox is present in the xaml and that it has a specific name.

Who is online

Users browsing this forum: Google [Bot], Semrush [Bot] and 68 guests