Vista Glass Effect for Movable Object in WPF - c#

I have the following glass effect which was adapted from WPF-MediaKit, the code for this is
public class GlassBehavior : Behavior<FrameworkElement>
{
private FrameworkElement attachedObject;
private readonly VisualBrush directVisualBrush = new VisualBrush();
private readonly Rectangle surrogateVisual = new Rectangle();
private readonly VisualBrush surrogateVisualBrush = new VisualBrush();
public GlassBehavior()
{
RenderOptions.SetEdgeMode(directVisualBrush, EdgeMode.Aliased);
RenderOptions.SetCachingHint(directVisualBrush, CachingHint.Cache);
RenderOptions.SetEdgeMode(surrogateVisualBrush, EdgeMode.Aliased);
RenderOptions.SetCachingHint(surrogateVisualBrush, CachingHint.Cache);
directVisualBrush.Stretch = Stretch.None;
surrogateVisualBrush.ViewboxUnits = BrushMappingMode.Absolute;
surrogateVisualBrush.ViewportUnits = BrushMappingMode.RelativeToBoundingBox;
surrogateVisualBrush.Viewport = new Rect(0, 0, 1, 1);
}
public static readonly DependencyProperty VisualProperty = DependencyProperty.Register("Visual",
typeof(Visual), typeof(GlassBehavior), new FrameworkPropertyMetadata(
null, new PropertyChangedCallback(OnVisualChanged)));
public Visual Visual
{
get { return (Visual)GetValue(VisualProperty); }
set { SetValue(VisualProperty, value); }
}
private static void OnVisualChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((GlassBehavior)d).OnVisualChanged(e);
}
protected virtual void OnVisualChanged(DependencyPropertyChangedEventArgs e)
{
SetupVisual();
}
public static readonly DependencyProperty EffectProperty =
DependencyProperty.Register("Effect", typeof(Effect), typeof(GlassBehavior),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnEffectChanged)));
public Effect Effect
{
get { return (Effect)GetValue(EffectProperty); }
set { SetValue(EffectProperty, value); }
}
private static void OnEffectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((GlassBehavior)d).OnEffectChanged(e);
}
protected virtual void OnEffectChanged(DependencyPropertyChangedEventArgs e)
{
SetEffect();
}
private void SetEffect()
{
if (surrogateVisual == null)
return;
surrogateVisual.Effect = Effect;
}
private void SetupVisual()
{
var element = Visual as FrameworkElement;
if (element == null || attachedObject == null)
return;
SetEffect();
surrogateVisualBrush.Visual = element;
surrogateVisual.Fill = surrogateVisualBrush;
directVisualBrush.Visual = surrogateVisual;
EnsureBrushSyncWithVisual();
}
private void EnsureBrushSyncWithVisual()
{
if (attachedObject == null || Visual == null)
return;
surrogateVisual.Width = attachedObject.ActualWidth;
surrogateVisual.Height = attachedObject.ActualHeight;
GeneralTransform trans = attachedObject.TransformToVisual(Visual);
Point pos = trans.Transform(new Point(0, 0));
var viewbox = new Rect
{
X = pos.X,
Y = pos.Y,
Width = attachedObject.ActualWidth,
Height = attachedObject.ActualHeight
};
surrogateVisualBrush.Viewbox = viewbox;
}
protected override void OnAttached()
{
if (attachedObject != null)
attachedObject.LayoutUpdated -= AssociatedObject_LayoutUpdated;
attachedObject = AssociatedObject;
PropertyInfo info = FindFillProperty(attachedObject);
if (info != null)
info.SetValue(attachedObject, directVisualBrush, null);
attachedObject.LayoutUpdated += AssociatedObject_LayoutUpdated;
SetupVisual();
base.OnAttached();
}
protected override void OnDetaching()
{
if (attachedObject != null)
attachedObject.LayoutUpdated -= AssociatedObject_LayoutUpdated;
base.OnDetaching();
}
private static PropertyInfo FindFillProperty(DependencyObject obj)
{
Type t = obj.GetType();
PropertyInfo info = t.GetProperty("Background") ?? t.GetProperty("Fill");
return info;
}
private void AssociatedObject_LayoutUpdated(object sender, EventArgs e)
{
EnsureBrushSyncWithVisual();
}
}
I then have the following XAML to adopt this effect
<Window ...>
<Grid>
<Grid x:Name="G0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="G1"
Margin="0,0,0,0"
Canvas.Left="0"
Canvas.Top="0"
Width="{Binding ActualWidth, ElementName=C1}"
Height="{Binding ActualHeight, ElementName=C1}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Image Source="large-snow-leopard-portrait-chris-boulton.jpg"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Stretch="UniformToFill"/>
</Grid>
<Border Margin="479,10,10,10"
Background="#FF545454"
BorderBrush="#FF000000"
BorderThickness="1,1,1,1"
CornerRadius="8,8,8,8">
<i:Interaction.Behaviors>
<Behaviors:GlassBehavior Visual="{Binding ElementName=G1, Mode=OneWay}">
<Behaviors:GlassBehavior.Effect>
<BlurEffect/>
<!--<ShaderEffectLibrary:PixelateEffect HorizontalPixelCounts="40" VerticalPixelCounts="40"/>-->
</Behaviors:GlassBehavior.Effect>
</Behaviors:GlassBehavior>
</i:Interaction.Behaviors>
<Border.Effect>
<DropShadowEffect BlurRadius="20" ShadowDepth="1"/>
</Border.Effect>
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Button" Margin="30,0" Height="30"/>
<Button Grid.Row="1" Content="Button" Margin="30,0" Height="30"/>
<Button Grid.Row="2" Content="Button" Margin="30,0" Height="30"/>
<Button Grid.Row="3" Content="Button" Margin="30,0" Height="30"/>
</Grid>
</Border>
</Grid>
</Grid>
</Window>
This produces
This is great, but I want to apply this blur to a movable ContentControl I am using so that I can drag my control over an image, and blur/redact someones face, in order to turn this image
into this image (note the blurry face).
To do this, I attempt to add a Canvas to the above XAML, and we have
...
<Grid>
<Grid x:Name="G0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="G1"
Margin="0,0,0,0"
Canvas.Left="0"
Canvas.Top="0"
Width="{Binding ActualWidth, ElementName=C1}"
Height="{Binding ActualHeight, ElementName=C1}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Image Source="large-snow-leopard-portrait-chris-boulton.jpg"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Stretch="UniformToFill"/>
</Grid>
<Canvas x:Name="C1"
Panel.ZIndex="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ContentControl
Width="130"
Height="130"
Canvas.Top="150"
Canvas.Left="150"
Selector.IsSelected="True"
Controls:TextBoxAttachedProperties.Text="Some Text Person #2"
Controls:TextBoxAttachedProperties.TextBoxBackground="DeepPink"
Controls:TextBoxAttachedProperties.TextBoxForeground="White"
Controls:ResizeThumb.HandleForeground="#2D2D30"
Controls:ResizeThumb.HandleForegroundHovered="#007ACC"
Controls:RotateThumb.HandleVisibility="Collapsed"
Controls:RotateThumb.HandleForeground="#656565"
Controls:RotateThumb.HandleForegroundHovered="#007ACC"
Style="{StaticResource MoveResizeRotateItemStyle}">
<Border Background="#FF545454" >
<i:Interaction.Behaviors>
<Behaviors:GlassBehavior Visual="{Binding ElementName=G1, Mode=OneWay}">
<Behaviors:GlassBehavior.Effect>
<ShaderEffectLibrary:PixelateEffect HorizontalPixelCounts="40" VerticalPixelCounts="40"/>
</Behaviors:GlassBehavior.Effect>
</Behaviors:GlassBehavior>
</i:Interaction.Behaviors>
</Border>
</ContentControl>
</Canvas>
</Grid>
</Grid>
</Window>
The problem is, I get an InvalidOperationException thrown from
GeneralTransform trans = attachedObject.TransformToVisual(Visual);
in the method EnsureBrushSyncWithVisual() of the GlassBehavior class.
System.InvalidOperationException: 'The specified Visual and this Visual do not share a common ancestor, so there is no valid transformation between the two Visuals.'
My question is
Q. How can I edit the GlassBehavior class so that I can apply this effect to my movable ContentControl, so that the moved control casts the desired blur on the hovered image?

Related

How can I use 2 data bindings for an ItemSource Data Template in WPF App

I have a counter which increments depending on a foreach Loop from:
public partial class UserControlCounter : UserControl, INotifyPropertyChanged
{
private int _scanStatusCounter;
public int ScanStatusCounter
{
get { return _scanStatusCounter; }
set { _scanStatusCounter = value; NotifyPropertyChanged(); }
}
public UserControlCounter()
{
InitializeComponent();
DataContext = this;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(getAll);
scanStatus.Text = "Persons " + ScanStatusCounter.ToString();
}
private async void getAll()
{
//grab data and iterate it
string[] string_array = new string[] { "a", "b", "c", "d", "e" }; // type = System.String[]
foreach (var i in string_array)
{
ScanStatusCounter++;
await Task.Delay(100);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and the Xaml:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Grid.Column="0" Name="myListBox" Height="40" ></ListBox>
<TextBlock Grid.Row="0" Grid.Column="1" Name="scanStatus" Height="40" Text="{Binding Path=ScanStatusCounter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBlock>
<Button Grid.Row="0" Grid.Column="2" Click="Button_Click" Height="40">Click Me</Button>
</Grid>
That works fine and increments "live", but my problem is that I need to get that now running inside a more complex Data Template.
I've tried to inject the Counter onClick with:
private async void Button_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
var contentPresenter = (btn.TemplatedParent as ContentPresenter);
var ppStatusCounter = contentPresenter.ContentTemplate.FindName("scanStatus", contentPresenter) as TextBlock;
ppStatusCounter.Text = "Entrys found: " + ScanStatusCounter.ToString();
}
But then I get the Value only onClick not like a live incrementing Counter, that's my Data Template:
<UserControl.Resources>
<c1:NameList x:Key="NameListData"/>
<DataTemplate x:Key="NameItemTemplate">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Domaine"/>
<TextBox Grid.Row="1" Grid.Column="0" x:Name="txtDomainName" Text="{Binding Path=DomaineName}" Margin="0,0,5,0"></TextBox>
<Button Grid.Row="1" Grid.Column="1" Width="100" Height="30" Margin="5,5,5,5" x:Name="btnNames" Click="Button_Click" Content="Start Scan" />
<Grid Grid.Column="2" Grid.Row="1" Height="20">
<ProgressBar x:Name="pbStatus" Height="20" Width="100" Minimum="0" Maximum="100" Visibility="Hidden"/>
<TextBlock x:Name="pbStatusText" HorizontalAlignment="Center" VerticalAlignment="Center" Text="Scanning" Visibility="Hidden"/>
</Grid>
<TextBlock Grid.Column="3" Grid.Row="1" Name="scanStatus" Text="{Binding Path=ScanStatusCounter, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="top">
<StackPanel Orientation="Horizontal" Margin="0,20,0,0">
<ListBox x:Name="lstDomainNames"
Margin="5,5,5,5"
ItemsSource="{Binding Source={StaticResource NameListData}}"
ItemTemplate="{StaticResource NameItemTemplate}"
IsSynchronizedWithCurrentItem="True"/>
</StackPanel>
</StackPanel>
</Grid>
that's my Data Source Class, not much there:
public partial class NameList : ObservableCollection<SetCredentials>
{
private static Logger logger = LogManager.GetCurrentClassLogger();
public NameList() : base()
{
using var forest = Forest.GetCurrentForest();
Forest currentForest = Forest.GetCurrentForest();
DomainCollection domains = currentForest.Domains;
foreach (Domain objDomain in domains)
{
Add(new SetCredentials(objDomain.ToString()));
}
}
}
public class SetCredentials
{
private string domainName;
public SetCredentials(string domain)
{
this.domainName = domain;
}
public string DomaineName
{
get { return domainName; }
set { domainName = value; }
}
}
Do I have to add a second Data Binding source to my ItemSource or do I need another approach for this, for example in my TextBox Binding?

WPF View not binding unless I switch view before

I'm trying to dev a WPF app which displays 3 views that we can switch using a side menu.
The first View displays articles. I've tried this view in another project on its own and it works just fine.
My issue:
The problem is that when I use it in my navigation (with the exact same code) the Images or Titles won't appear unless I click on another view first then click back.
Here's my current code:
Model: (Article)
public class Article : INotifyPropertyChanged
{
int id;
string title;
string link;
BitmapImage image;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Article(int id, string title, string link, BitmapImage image)
{
Id = id;
Title = title;
Link = link;
Image = image;
}
public int Id
{
get => id;
set { if (value != id) { id = value; NotifyPropertyChanged(); } }
}
public string Title
{
get => title;
set { if (value != title) { title = value; NotifyPropertyChanged(); } }
}
public string Link
{
get => link;
set { if (value != link) { link = value; NotifyPropertyChanged(); } }
}
public BitmapImage Image
{
get => image;
set { if (value != image) { image = value; NotifyPropertyChanged(); } }
}
}
ViewModel: (ArticlesViewModel)
public class ArticlesViewModel : INotifyPropertyChanged
{
Article[] articles;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public AccueilViewModel()
{
loadArticle();
}
public Article[] Articles
{
get => articles;
set { if (value != articles) { articles = value; NotifyPropertyChanged(); } }
}
private async void loadArticle()
{
var client = new WordPressClient(App.WordpressLink);
try
{
var asychArticles = GetArticles(client);
await asychArticles;
var articles = asychArticles.Result;
int i = 0;
int a;
foreach (WordPressPCL.Models.Post p in articles)
{
a = p.FeaturedMedia.Value;
var media = GetMedia(client, a);
await media;
BitmapImage bimage = App.getImage(media.Result.SourceUrl);
articleImg[i].Source = bimage;
articleTitle[i].Content = p.Title.Rendered.ToUpper();
articleLink[i] = p.Link;
i++;
if (i == 4) break;
}
}
catch (System.Net.Http.HttpRequestException e)
{
}
}
static async Task<IEnumerable<WordPressPCL.Models.Post>> GetArticles(WordPressClient client)
{
var posts = await client.Posts.GetAll();
return posts;
}
static async Task<WordPressPCL.Models.MediaItem> GetMedia(WordPressClient client, int number)
{
var media = await client.Media.GetByID(number);
return media;
}
}
View: (ArticlesView)
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="5*"/>
</Grid.RowDefinitions>
<Grid Name="Article1" Grid.Row="0" Margin="10,10,10,10" DataContext="{Binding Articles[0]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image1" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre1" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Name="Article2" Grid.Column="0" Margin="10,10,10,10" DataContext="{Binding Articles[1]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image2" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre2" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
<Grid Name="Article3" Grid.Column="1" Margin="10,10,10,10" DataContext="{Binding Articles[2]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image3" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre3" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
<Grid Name="Article4" Grid.Column="2" Margin="10,10,10,10" DataContext="{Binding Articles[3]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image4" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre4" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
</Grid>
</Grid>
MainWindow.xaml:
<Window.Resources>
<DataTemplate x:Name="ArticlesViewTemplate" DataType="{x:Type viewmodels:ArticlesViewModel}">
<views:ArticlesView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Feature2ViewTemplate" DataType="{x:Type viewmodels:Feature2ViewModel}">
<views:Feature2View DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Feature3ViewTemplate" DataType="{x:Type viewmodels:Feature3ViewModel}">
<views:Feature3View DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel Grid.Row="1" VerticalAlignment="Top" Margin="20,20,20,0">
<Button Content="Articles" Click="F1Button_Click"/>
<Button Content="F2" Click="F2Button_Click"/>
<Button Content="F3" Click="F3Button_Click"/>
</StackPanel>
</Grid>
<ContentControl Grid.Column="1" Content="{Binding}"/>
</Grid>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
ArticlesViewModel avm;
Feature2ViewModel f2vm;
Feature3ViewModel f3vm;
public MainWindow()
{
InitializeComponent();
}
private void F1Button_Click(object sender, RoutedEventArgs e)
{
if (avm == null) avm = new AccueilViewModel();
DataContext = avm;
}
private void F2Button_Click(object sender, RoutedEventArgs e)
{
if (f2vm == null) f2vm = new CatalogueViewModel();
DataContext = f2vm;
}
private void F3Button_Click(object sender, RoutedEventArgs e)
{
if (f3vm == null) f3vm = new MesFormationsViewModel();
DataContext = f3vm;
}
}
To sum up:
Click on Articles -> Nothing is displayed
Click on F2 then Articles -> Nothing is displayed
Click on Articles then F2 then Articles -> Images and Titles correctly displayed!
Any ideas on where the problem might be coming from? Thank you.
So the problem seemed to be coming from the fact that I was using an array. At the start the DataContext was null so the images and titles even though they were loaded in the modelview were not displayed on the view.
The problem was fixed by using an ObservableCollection instead of the array. If I understood correctly, NotifyPropertyChanged() is apparently not notifying the view when the array is at first null.(?)

Get index of data binding from ItemsControl

Is there any way to get the index of the data which is binded to the ItemsControl, when a button is clicked?
<ItemsControl x:Name="ic_schranke" DataContext="{Binding Schranke}" >
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Height="80" x:Name="btn_open" Click="btn_open_Click" HorizontalAlignment="Stretch" Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="30*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Rectangle x:Name="rect" Grid.ColumnSpan="3" Grid.Row="1" Opacity="0.65" Grid.Column="0" Fill="#FFCEEAFF"/>
<Border CornerRadius="25" Height="50" Width="50" HorizontalAlignment="Center" Grid.Column="0" Grid.Row="1">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name" Grid.Column="1" Grid.Row="1" Text="{Binding name}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="ExtraLight" Foreground="Black" />
</Grid>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the sample data which is binded to the IC:
List<Schranke> elements;
public UserPage()
{
this.InitializeComponent();
elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { name = $"Schranke NR:{i}" });
this.ic_schranke.ItemsSource = elements;
}
And here is the code in the button click event:
private async void btn_open_Click(object sender, RoutedEventArgs e)
{
Grid grid = (sender as Button).Content as Grid;
//Debug.WriteLine($"content {tb.Text} clicked");
var tmp_background = grid.Background;
grid.Background = new SolidColorBrush(new Windows.UI.Color() { A = 255, R = 78, G = 170, B = 44 });
VibrationDevice testVibrationDevice = VibrationDevice.GetDefault();
testVibrationDevice.Vibrate(System.TimeSpan.FromMilliseconds(150));
await Task.Delay(3000);
grid.Background = tmp_background;
}
Maybe something along the lines of this:
the presenter - put this in the data context
public class SchrankePresenter : INotifyPropertyChanged
{
private List<Schranke> _elements;
public List<Schranke> Elements
{
get { return _elements; }
set
{
_elements = value;
OnPropertyChanged("Elements");
}
}
public ICommand ClickCommand { get; set; }
private void OnPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
public SchrankePresenter()
{
var elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { Name = $"Schranke NR:{i}" });
Elements = elements;
ClickCommand = new DelegateCommand(ClickAction);
}
public void ClickAction(Schranke item)
{
VibrationDevice.GetDefault().Vibrate(TimeSpan.FromMilliseconds(150));
}
}
public class Schranke
{
public string Name { get; set; }
}
the template:
<ListView ItemsSource="{Binding Elements}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Height="80"
HorizontalAlignment="Stretch"
Command="{Binding ClickCommand}"
Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="30*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Rectangle x:Name="rect"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Fill="#FFCEEAFF"
Opacity="0.65" />
<Border Grid.Row="1"
Grid.Column="0"
Width="50"
Height="50"
HorizontalAlignment="Center"
CornerRadius="25">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name"
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="ExtraLight"
Foreground="Black"
Text="{Binding Name}" />
</Grid>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
you can do the background animation using a storyboard/visualstate
EDIT : regarding the DelegateCommand, it's a simple implementation I use for my WPF apps -
internal class DelegateCommand<T> : ICommand
{
private Action<T> _clickAction;
public DelegateCommand(Action<T> clickAction)
{
_clickAction = clickAction;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_clickAction((T)parameter);
}
}

How to drag and drop a "box" in silverlight

I have a created a box like this and now i'm trying to drag and drop the box, with rectangles and other objects I did it, but with this I don't know how to do.
Here is the code of how I did the box
XAML:
<Canvas>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Text="{Binding Header,UpdateSourceTrigger=PropertyChanged}"
BorderBrush="Black" BorderThickness="1" Canvas.Left="41" Canvas.Top="10" Width="97" />
<TextBox Text="{Binding Text,UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto"
AcceptsReturn="True"
BorderBrush="Black" BorderThickness="1" Grid.Row="1" Canvas.Left="41" Canvas.Top="39" Height="53" Width="97" />
</Grid>
</Canvas>
The c# code:
public partial class MyBox : UserControl
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(string), typeof(MyBox),null);
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Content", typeof(string), typeof(MyBox),null);
public string Header
{
get { return GetValue(HeaderProperty) as string; }
set { SetValue(HeaderProperty, value); }
}
public string Text
{
get { return GetValue(TextProperty) as string; }
set { SetValue(TextProperty, value); }
}
public MyBox()
{
InitializeComponent();
this.DataContext = this;
}
And this is the code for adding another box:
private void Button_Click(object sender, RoutedEventArgs e)
{
panel.Children.Add(new MyBox
{
//LayoutRoot.Children.Add(new MyBox {
Header = "Another box",
Text = "...",
// BorderBrush = Brushes.Black,
BorderThickness = new Thickness(1),
Margin = new Thickness(10)
});
}
Here is a sample, inspired from https://stackoverflow.com/a/1495486/145757 (thanks Corey), slightly adapted, simplified (no additional boolean) and enhanced (take margins into account) for our use-case:
First I've modified the box so that it has a dedicated drag area:
<UserControl x:Class="WpfApplication1.MyBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Drag me" />
<TextBox Text="{Binding Header,UpdateSourceTrigger=PropertyChanged}"
BorderBrush="Black" BorderThickness="1" Margin="2" Grid.Row="1" />
<TextBox Text="{Binding Text,UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto"
AcceptsReturn="True"
BorderBrush="Black" BorderThickness="1" Margin="2" Grid.Row="2" />
</Grid>
</UserControl>
MainWindow XAML slightly modified:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:WpfApplication1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Canvas x:Name="panel">
</Canvas>
<Button Content="Add" Grid.Row="1" Grid.Column="0" Click="Button_Click" />
</Grid>
</Window>
And the drag-and-drop engine is in the code-behind:
using System.Windows;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MyBox box = new MyBox
{
Header = "Another box",
Text = "...",
BorderBrush = Brushes.Black,
BorderThickness = new Thickness(1),
Margin = new Thickness(10)
};
box.MouseLeftButtonDown += Box_MouseLeftButtonDown;
box.MouseLeftButtonUp += Box_MouseLeftButtonUp;
box.MouseMove += Box_MouseMove;
panel.Children.Add(box);
}
private MyBox draggedBox;
private Point clickPosition;
private void Box_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
draggedBox = sender as MyBox;
clickPosition = e.GetPosition(draggedBox);
draggedBox.CaptureMouse();
}
private void Box_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
draggedBox.ReleaseMouseCapture();
draggedBox = null;
}
private void Box_MouseMove(object sender, MouseEventArgs e)
{
if (draggedBox != null)
{
Point currentPosition = e.GetPosition(panel);
draggedBox.RenderTransform = draggedBox.RenderTransform ?? new TranslateTransform();
TranslateTransform transform = draggedBox.RenderTransform as TranslateTransform;
transform.X = currentPosition.X - clickPosition.X - draggedBox.Margin.Left;
transform.Y = currentPosition.Y - clickPosition.Y - draggedBox.Margin.Right;
}
}
}
}
have a look at Blend Interaction behaviours. I did a sample a while back http://invokeit.wordpress.com/2012/02/10/wp7-drag-drop-example/

Avoid clipping when silverlight/wpf userControl is dynamically added

I am developing a UserControl that consists of a block with a heading and a list of items (ItemsControl). The problem is that the user control contents gets clipped off when I dynamically add the usercontrol to a canvas as shown below. I am not setting a size for the usercontrol internally. What can be done to avoid this.
Clipping is happening even when I drag and drop the usercontrol to canvas. I need the contents to get scaled.
<UserControl x:Class="MyTools.MyControl"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid x:Name="LayoutRoot" Background="White">
<Border Name="MainBorder" CornerRadius="5" BorderThickness="2" BorderBrush="Black">
<Grid Name="grid1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<Grid.RowDefinitions>
<RowDefinition Height="34" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Name="titleGrid" Grid.Row="0" Background="#FF727272">
<TextBlock Name="titleText" HorizontalAlignment="Center" Text="{Binding ControlName}" VerticalAlignment="Center" FontSize="13" FontWeight="Bold" Foreground="Beige" />
</Grid>
<Grid Name="gridpr" Grid.Row="1" Background="#12C48F35">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Name="borderPr" CornerRadius="3" Margin="10" BorderThickness="1" BorderBrush="LightGray" Grid.Row="0">
<Grid Name="gridPr" Background="#FFC1C1C1" MouseLeftButtonUp="gridPr_MouseLeftButtonUp">
<StackPanel>
<TextBlock HorizontalAlignment="Center" Name="txtPr" Text="SubItems" VerticalAlignment="Center" Foreground="#FF584848" FontSize="12" />
<ItemsControl x:Name="pitems" ItemsSource="{Binding MyItems}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="15,0,0,0">
<TextBlock Text="{Binding MyVal}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</Border>
</Grid>
</Grid>
</Border>
</Grid>
</UserControl>
Code-behind for UserControl:
namespace MyTools
{
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public string ControlName { get; set; }
public object MyItems { get; set; }
public class Row
{
public string MyVal { get; set; }
}
protected override Size MeasureOverride(Size availableSize)
{
var desiredSize = base.MeasureOverride(availableSize);
var sideLength = Math.Min(desiredSize.Width, desiredSize.Height);
desiredSize.Width = sideLength;
desiredSize.Height = sideLength;
return desiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
var sideLength = Math.Min(this.DesiredSize.Width, this.DesiredSize.Height);
return base.ArrangeOverride(new Size(sideLength, sideLength));
}
private void gridPr_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
pitems.Visibility = pitems.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
}
}
}
Host Client Code:
namespace TestProject
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
MyControl control1 = new MyControl();
control1.ControlName = "Test Name";
var test = new List<MyControl.Row>(
new MyControl.Row[]
{
new MyControl.Row {MyVal = "Item1"},
new MyControl.Row {MyVal = "Item2"},
new MyControl.Row {MyVal = "Item3"}
});
control1.MyItems = test;
control1.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
MessageBox.Show(control1.DesiredSize.Height.ToString());
canvas1.Children.Add(control1);
}
}
}
Layout of host:
<UserControl x:Class="TestProject.MainPage"
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"
d:DesignHeight="503" d:DesignWidth="758" xmlns:my="clr-namespace:MyTools">
<Grid x:Name="LayoutRoot" Background="White" Height="507" Width="757">
<Canvas Height="503" Name="canvas1" HorizontalAlignment="Left" Margin="-1,-1,0,0" VerticalAlignment="Top" Width="758">
</Canvas>
</Grid>
</UserControl>
EDIT:
Turns out that the issue is because of MeasureOverride. The clipping is not there when I removed it. Thanks for the suggestions.

Categories