I have several pages that include the same border and its child elements.
Each page has
<Border Grid.Column="1" Style="{StaticResource InstructionBox}" x:name="staticBorder">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Style="{StaticResource OnlyContentStyle}" Grid.Row="0" Grid.Column="0"
Click="Instruction_Click">
<Border >
<TextBlock Style="{StaticResource InstructionStyle}">
</TextBlock>
</Border>
</Button>
<Button Style="{StaticResource OnlyContentStyle}" Grid.Row="0" Grid.Column="1"
Click="Logs_Click">
<Border >
<TextBlock Style="{StaticResource LogStyle}">
</TextBlock>
</Border>
</Button>
<Border Grid.Row="2" Grid.ColumnSpan="2" x:Name="InstructionBorder">
<StackPanel x:Name="PanelInstructions" Style="{StaticResource InstructionTextStyle}">
</StackPanel>
</Border>
</Grid>
</Border>
Is there a way to make reference this border throughout all my pages?
Thanks
No, there is no direct way. XAML only allows an element to exist in a single location on the Visual Tree. To do this you need to convert your border into a user control. There are several ways to do this. In the example below, I've used dependency properties to provide variable content for the first button in the user control's code behind. For the click event, I recommend binding the buttons Command to an ICommand implementation, but you need to be doing MVVM for that. This sample adds a RoutedEventHandler instead.
public static readonly DependencyProperty InstructionButtonContentProperty = DependencyProperty.Register(
"InstructionButtonContent", typeof(FrameworkElement), typeof(InstructionBoxControl), new PropertyMetadata(default(FrameworkElement)));
public FrameworkElement InstructionButtonContent
{
get { return (FrameworkElement) GetValue(InstructionButtonContentProperty); }
set { SetValue(InstructionButtonContentProperty, value); }
}
public event RoutedEventHandler InstructionButtonClicked;
public InstructionBoxControl()
{
InitializeComponent();
}
private void InstructionButtonClick(object sender, RoutedEventArgs e)
{
InstructionButtonClicked?.Invoke(sender, e);
}
For the most part you can paste the border into the user control's XAML. Then you need to bind the dynamic parts to the user control's DependencyProperties. You also hook up the button's click event call the RoutedEventHandler in the code behind above.
<Border >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="20" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
Click="InstructionButtonClick">
<Border>
<ContentPresenter Content="{Binding Path=InstructionButtonContent, RelativeSource={RelativeSource AncestorType={x:Type local:InstructionBoxControl}}}"/>
</Border>
</Button>
You can then use the new UserControl inplace of the border on any page like so:
<local:InstructionBoxControl InstructionButtonClicked="InstructionBoxControl_OnInstructionButtonClicked">
<local:InstructionBoxControl.InstructionButtonContent>
<TextBlock>Instructions</TextBlock>
</local:InstructionBoxControl.InstructionButtonContent>
</local:InstructionBoxControl>
The other button and satckpanel should work similarly.
Related
I have a TabView whose ItemTemplate is like this:
<controls:TabView.ItemTemplate>
<DataTemplate x:DataType="data:Playlist">
<local:HeaderedPlaylistControl
IsPlaylist="True"
Loaded="HeaderedPlaylistControl_Loaded"
MusicCollection="{x:Bind Mode=OneWay}" />
</DataTemplate>
</controls:TabView.ItemTemplate>
This is part of the HeaderedPlaylistControl:
<local:PlaylistControl
AllowReorder="False"
AlternatingRowColor="True"
ItemsSource="{x:Bind MusicCollection.Songs, Mode=OneWay}">
<local:PlaylistControl.Header>
<controls:ScrollHeader Mode="Sticky">
<UserControl>
<Grid
x:Name="PlaylistInfoGrid"
Padding="10"
Background="{ThemeResource SystemColorHighlightColor}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
x:Name="PlaylistCover"
Grid.RowSpan="3"
Width="180"
Height="180"
Margin="20"
Source="Assets/monotone_bg_wide.png" />
<TextBlock
x:Name="PlaylistNameTextBlock"
Grid.Column="1"
Margin="0,5"
VerticalAlignment="Center"
FontSize="36"
Foreground="White"
Style="{StaticResource HeaderTextBlockStyle}"
Text="{x:Bind MusicCollection.Name, Mode=OneWay}" />
<TextBlock
x:Name="PlaylistInfoTextBlock"
Grid.Row="1"
Grid.Column="1"
Margin="0,5"
VerticalAlignment="Top"
Foreground="White"
Text="{x:Bind MusicCollection.Songs, Converter={StaticResource SongCountConverter}, Mode=OneWay}" />
</Grid>
</UserControl>
</controls:ScrollHeader>
</local:PlaylistControl.Header>
</local:PlaylistControl>
When I switch between tabs, the HeaderedPlaylistControl doesn't update it's content. Why is that?
Is it because of the MusicCollection property (it is of type Playlist) doesn't notify the binding when switching tabs? If so, where should I put the notification? The definition of Playlist is here.
HeaderedPlaylistControl is here:
Yes, as you suspected, the problem is that the MusicCollection property is an ordinary property which does not notify about changes. To make your code work, you need to make the MusicCollection property a dependency property (see docs). This is a kind of properties which are best for data-bound properties of visual controls and have many additional features as well.
public static readonly DependencyProperty MusicCollectionProperty =
DependencyProperty.Register(
nameof(MusicCollection), typeof(Playlist),
typeof(HeaderedPlaylistControl), null
);
public Playlist MusicCollection
{
get { return (bool)GetValue(MusicCollectionProperty); }
set { SetValue(MusicCollectionProperty, value); }
}
I have a DataTemplate like this:
<!-- MULTI VIEW TEMPLATE -->
<DataTemplate x:Key="MultiViewTemplate" >
<Grid x:Name="MultiViewGrid" ShowGridLines="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl Content="{StaticResource BorderHwnd1}" Grid.Column="0" Grid.Row="0" x:Name="MultiViewBorder1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MouseDoubleClick="MultiViewBorder1_MouseDoubleClick"/>
<ContentControl Content="{StaticResource BorderHwnd2}" Grid.Column="1" Grid.Row="0" x:Name="MultiViewBorder2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MouseDoubleClick="MultiViewBorder2_MouseDoubleClick"/>
<ContentControl Content="{StaticResource BorderHwnd3}" Grid.Column="0" Grid.Row="1" x:Name="MultiViewBorder3" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MouseDoubleClick="MultiViewBorder3_MouseDoubleClick"/>
<ContentControl Content="{StaticResource BorderHwnd4}" Grid.Column="1" Grid.Row="1" x:Name="MultiViewBorder4" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MouseDoubleClick="MultiViewBorder4_MouseDoubleClick"/>
</Grid>
</DataTemplate>
And the BorderHwnd1 is like this:
<Border x:Key="BorderHwnd1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" BorderBrush="Transparent" BorderThickness="0" Background="Transparent" >
<Image Margin="1" Source="/MyProject;component/Images/No_image_available.png" />
</Border>
My problem is that MultiViewBorder1_MouseDoubleClick event does not work after I remove and assign programmatically a new child to MultiViewBorder1. Initially MultiViewBorder1.Child is an image (No_image_available.png). In my code, I remove the image and assign a new child. This new child is a HwndHost.
Where is my mistake? any help is appreciated!
The child is changing from a WPF Image control....which is able to raise/flow "routed" "mouse" events through the visual tree....to some "hwnd" which you have inside the HwndHost, which is handling raw WIN32 mouses messages....and doesn't route them.
To be able to get mouse activity in your HwndHost into the visual tree, you could use a few different techniques to get them routed.
have an overlayed "transparent" window
handle the "mouse" messages in your WIN32/Hwnd, and then use "delegates" to raise
How can I convert Win32 mouse messages to WPF mouse events?
WPF WIN32 hwndhost WM_MOUSEMOVE WM_MOUSEHOVER
I have made a simple RSS Feed app using AppStudio, and as the title suggests, I need some help on implementing DataTemplateSelector.
I want the first and the last items of the feed to use "BigCards" item template, while the others to have the "SmallCards" item template.
Here is the code for the item template styling:
<!-- BigCards Item -->
<DataTemplate x:Key="BigCards">
<Grid Style="{StaticResource BoxGrid}" Margin="0,0,0,10" Height="380">
<Rectangle Width="900"/>
<Grid Style="{StaticResource BoxGrid}">
<Grid.RowDefinitions>
<RowDefinition Height="280"/>
<RowDefinition Height="220"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding ImageUrl}" Stretch="UniformToFill" Margin="0,4,0,0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Grid Grid.Row="1" Height="220" Margin="10,10,10,10">
<Grid Height="210">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Style="{StaticResource BoxTitleStyle}" Text="{Binding Title}" MaxLines="2"/>
</Grid>
</Grid>
</Grid>
</Grid>
</DataTemplate>
<!-- SmallCards Item -->
<DataTemplate x:Key="SmallCards">
<Grid Height="120" Margin="0,0,0,10" Style="{StaticResource BoxGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding ImageUrl}" Stretch="UniformToFill" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Grid Grid.Column="1">
<Rectangle Width="900" Height="0"/>
<Grid Margin="16,5,16,5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Style="{StaticResource BoxTitleStyle}" Text="{Binding Title}" MaxLines="2"/>
<TextBlock Grid.Row="1" Margin="0,5,0,0" Style="{StaticResource BoxSubtitleStyle}" Text="{Binding Summary}"/>
</Grid>
</Grid>
</Grid>
Here is some code behind Mainpage.xaml.cs which is needed as a trigger between these item templates:
//Listim dinamik i artikujve
public abstract class TemplateSelector : ContentControl
{
public abstract DataTemplate SelectTemplate(object item, DependencyObject container);
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
ContentTemplate = SelectTemplate(newContent, this);
}
}
//endof Abstract Class
public class ArticleTemplateSelector : TemplateSelector
{
public DataTemplate BigCards
{
get;
set;
}
public DataTemplate SmallCards
{
get;
set;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
// Determine which template to return;
return null;
}
}
I would really appreciate if someone could help me on what I should insert in the DataTemplate SelectTemplate class so the first and the last item on my RSS Feed will use BigCards template, while the others will use SmallCards...?
Here is the Main Page xaml which as for the moment uses the BigCards template:
<Hub x:Name="Container" Grid.Row="1" Margin="0,28,0,0" Background="{StaticResource AppBackground}" DataContext="{Binding}" SectionsInViewChanged="OnSectionsInViewChanged">
<HubSection x:Name="LajmetSection" Padding="0,-30,20,0" Width="400" DataContext="{Binding MainViewModel.LajmetModel}"
d:DataContext="{d:DesignData Source=/Assets/Data/LajmetDataSource.json, Type=vm:LajmetViewModel, IsDesignTimeCreatable=true}"
ContentTemplate="{StaticResource BigCards}" IsHeaderInteractive="{Binding HasMoreItems}" />
</Hub>
Regards
I solved it in a different way. I modified the RSS page of my website in a way which would assign a static author for every 10th post. In my case I assigned every 10th post to "X" author. Then I implemented this simple statement
if (RssSchema.Author==X)
use BigCards;
else
use SmallCards;
which does the job quite nicely. If anyone needs help feel free to contact me.
I gave up on my code bugs so I decide to write here. I really hope to get a solution. What I want with the listbox is: when i click the button, it will retrieve data from database then load it into the listbox. It worked fine. But when I add wpf style, it started problem because I want to add image into each item next to text - image (right side) and text (left side). The result in the listbox is blank but actually it seems there is a data list - please look at the picture. I may have done something wrong in my code or wpf. I am not sure what is problem.... I would appreciate if you can have a look at my code. Your given code would be much helpful. Thanks alot!
WPF:
<ListBox Name="lstDinner" DisplayMemberPath="Name" Margin="513,85,608,445" Style="{DynamicResource ResourceKey=styleListBox}"/>
WPF STYLE:
<DataTemplate x:Key="templateListBoxItem">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Margin="0,0,10,0">
<Image Source="{Binding Path=Image}"
Stretch="Fill"
Height="40"
Width="40"></Image>
</Border>
<TextBlock Text="{Binding Path=Name}"
FontWeight="Bold"
Grid.Column="1"
Grid.Row="0"></TextBlock>
<TextBlock Text="{Binding Path=About}"
Grid.Column="1"
Grid.Row="1"></TextBlock>
</Grid>
</DataTemplate>
<Style x:Key="styleListBox" TargetType="{x:Type ListBox}">
<Setter Property="ItemTemplate" Value="{StaticResource ResourceKey=templateListBoxItem}"></Setter>
</Style>
C#:
private void Button_Click(object sender, RoutedEventArgs e)
{
_dinnerExtractor = new DinnerExtractor();
const int oneDay = 1;
_databaseDinnerList = new ObservableCollection<FoodInformation>(_dinnerExtractor.GetDinnerDays(oneDay));
if (_databaseDinnerList != null)
{
foreach (var list in _databaseDinnerList)
{
lstDinner.Items.Add(new FoodInformation { Dinner = list.Dinner, DinnerImage = list.DinnerImage});
}
//lstDinner.ItemsSource = _databaseDinnerList;
}
}
FoodInformation class does not contains properties: Image and Name (you are trying binding to these properties in DataTemplate).
From code-behind we can create definition of FoodInformation class with properties Dinner and DinnerImage:
class FoodInformation
{
public string Dinner { get; set; }
public ImageSource DinnerImage { get; set; }
}
So you should binding to properties Dinner and DinnerImage, not to Image and Name.
If you change in DataTemplate appropriate properties names everything will be ok.
<DataTemplate x:Key="templateListBoxItem">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Margin="0,0,10,0">
<Image Source="{Binding Path=DinnerImage }"
Stretch="Fill"
Height="40"
Width="40"></Image>
</Border>
<TextBlock Text="{Binding Path=Dinner }"
FontWeight="Bold"
Grid.Column="1"
Grid.Row="0"></TextBlock>
</Grid>
</DataTemplate>
I have a MediaElement inside ListBox.How I can get access to "audiop_Copy" by buttons "play/pause"?
<local:TypeTemplateSelector.WithAudio>
<DataTemplate>
<Grid Margin="0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1">
<TextBlock ... />
<StackPanel Height="50" Orientation="Horizontal" Margin="5,0,4,0" MinHeight="50">
</TextBlock>
<Button Click="PlayMedia" Content="Play" />
<Button Click="PauseMedia" Content="Pause" />
</StackPanel>
<MediaElement Name="audiop_Copy" Source="{Binding audioUri}" Stretch="None" HorizontalAlignment="Left" AutoPlay="False"/>
</StackPanel>
</Grid>
</DataTemplate>
</local:TypeTemplateSelector.WithAudio>
2 ways to do it from the spot (possibly there are more). You will need a pointer to your Button that was clicked anyway:
[difficult, inflexible, fragile] In button Click event handler use VisualTreeHelper class to navigate Visual Tree and find the element. Use sender as starting point
[better solution] use Tag property and binding.
<Button Click="PauseMedia" Content="Pause" Tag={Binding ElementName=audiop_Copy} />
And in handler something like that:
private void PauseMedia(object sender, RoutedEventArgs e)
{
var me = ((FrameworkElement) sender).Tag as MediaElement;
}