WPF

2010-08-04 22 views
11

'de çalışma zamanında oluşturulan kontroller Bu popüler bir sorudur, ancak tam olarak cevap veren bir şey bulamadım, ancak aramalarımda bir şey özlediysem özür dilerim. Ben oluşturmak ve sonra aşağıdaki kodu kullanarak çalışma zamanında bir kontrol ölçümü için çalışıyorumWPF

(ölçümler daha sonra tarzı marquee için denetimleri kaydırma kullanılacaktır - Her bir kontrol farklı bir boyutudur):

Label lb = new Label(); 
lb.DataContext= task; 
Style style = (Style)FindResource("taskStyle"); 
lb.Style = style; 

cnvMain.Children.Insert(0,lb); 

width = lb.RenderSize.Width; 
width = lb.ActualWidth; 
width = lb.Width; 

Kod, bir Etiket denetimi oluşturur ve ona bir stil uygular. Stil, "görev" nesnesine bağlanan kontrol şablonumu içerir.

lb.Width = NaN 
lb.RenderSize.Width = 0 
lb.ActualWidth = 0 

var mı uygulanıyor: Ben öğesi oluşturduğunuzda ben şu sonuçları (I gezinmek ve sırayla her özelliği incelemek) olsun yukarıdaki Ben özelliklerden herhangi birini kullanarak kontrolünü ölçmek için deneyin ancak zaman mükemmel görünür Çalışma zamanında oluşturduğunuz bir kontrolün oluşturulmuş yüksekliğini ve genişliğini elde etmenin yolu nedir?

GÜNCELLEME: cevaplar olarak çözümlerinizi kaldırarak için

üzgünüm. Kurduğum basit bir örnek sistem üzerinde çalışıyorlar ama tam çözümümle değil.

Sanırım bu tarzla ilgili bir şey olabilir, bu karışıklık için çok üzüldüm, ama burada tüm şeyi yapıştırıyorum.

Birincisi, kaynaklar:

<Storyboard x:Key="mouseOverGlowLeave"> 
     <DoubleAnimation From="8" To="0" Duration="0:0:1" BeginTime="0:0:2" Storyboard.TargetProperty="GlowSize" Storyboard.TargetName="Glow"/> 
    </Storyboard> 

    <Storyboard x:Key="mouseOverTextLeave"> 
     <ColorAnimation From="{StaticResource buttonLitColour}" To="Gray" Duration="0:0:3" Storyboard.TargetProperty="Color" Storyboard.TargetName="ForeColour"/> 
    </Storyboard> 

    <Color x:Key="buttonLitColour" R="30" G="144" B="255" A="255" /> 

    <Storyboard x:Key="mouseOverText"> 
     <ColorAnimation From="Gray" To="{StaticResource buttonLitColour}" Duration="0:0:1" Storyboard.TargetProperty="Color" Storyboard.TargetName="ForeColour"/> 
    </Storyboard> 

Ve stil kendisi:

<Style x:Key="taskStyle" TargetType="Label"> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate> 
        <Canvas x:Name="cnvCanvas"> 
         <Border Margin="4" BorderBrush="Black" BorderThickness="3" CornerRadius="16"> 
          <Border.Background> 
           <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> 
            <GradientStop Offset="1" Color="SteelBlue"/> 
            <GradientStop Offset="0" Color="dodgerBlue"/> 
           </LinearGradientBrush> 
          </Border.Background> 
          <DockPanel Margin="8"> 
           <DockPanel.Resources> 
            <Style TargetType="TextBlock"> 
             <Setter Property="FontFamily" Value="corbel"/> 
             <Setter Property="FontSize" Value="40"/> 
            </Style> 
           </DockPanel.Resources> 
           <TextBlock VerticalAlignment="Center" Margin="4" Text="{Binding Path=Priority}"/> 
           <DockPanel DockPanel.Dock="Bottom"> 
            <Border Margin="4" BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4"> 
             <TextBlock Margin="4" DockPanel.Dock="Right" Text="{Binding Path=Estimate}"/> 
            </Border> 
            <Border Margin="4" BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4"> 
             <TextBlock Margin="4" DockPanel.Dock="Left" Text="{Binding Path=Due}"/> 
            </Border> 
           </DockPanel> 
           <DockPanel DockPanel.Dock="Top"> 
            <Border Margin="4" BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4"> 
             <TextBlock Margin="4" Foreground="LightGray" DockPanel.Dock="Left" Text="{Binding Path=List}"/> 
            </Border> 
            <Border Margin="4" BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4"> 
             <TextBlock Margin="4" Foreground="White" DockPanel.Dock="Left" Text="{Binding Path=Name}"/> 
            </Border> 
           </DockPanel> 
          </DockPanel> 
         </Border> 
        </Canvas> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 

Ayrıca, kod kullanıyorum:

Label lb = new Label(); //the element I'm styling and adding 
    lb.DataContext= task; //set the data context to a custom class 
    Style style = (Style)FindResource("taskStyle"); //find the above style 
    lb.Style = style; 

    cnvMain.Children.Insert(0,lb); //add the style to my canvas at the top, so other overlays work 
    lb.UpdateLayout(); //attempt a full layout update - didn't work 
    Dispatcher.Invoke(DispatcherPriority.Render, new Action(() => 
    { 
     //Synchronize on control rendering 
     double width = lb.RenderSize.Width; 
     width = lb.ActualWidth; 
     width = lb.Width; 
    })); //this didn't work either 
    lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); //nor did this 
    double testHeight = lb.DesiredSize.Height; 
    double testWidth = lb.RenderSize.Width; //nor did this 
    width2 = lb.ActualWidth; //or this 
    width2 = lb.Width; //or this 

//positioning for my marquee code - position off-screen to start with 
    Canvas.SetTop(lb, 20); 
    Canvas.SetTop(lb, -999); 

//tried it here too, still didn't work 
    lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 
    testHeight = lb.DesiredSize.Height; 
//add my newly-created label to a list which I access later to operate the marquee 
    taskElements.AddLast(lb); 
//still didn't work 
    lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 
    testHeight = lb.DesiredSize.Height; 
//tried a mass-measure to see if that would work - it didn't 
    foreach (UIElement element in taskElements) 
    { 
     element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 
    } 

herhangi Her zaman Bu denir, değer ya 0 olarak döndürülür veya bir sayı değil, henüz etiket oluşturulduğunda görünür olduğu gibi bir boyutu vardır. Etiketi ekranda göründüğünde, ancak bu aynı sonuçları üreten bir tuşa basarak bu kodu çalıştırmayı denedim.

Kullanmakta olduğum stil yüzünden mi? Databindings belki? Yaptığım şey yanlış bir şekilde açık mıdır yoksa WPF Gremlinler sadece gerçekten benden nefret mi ediyor?

İkinci Güncelleme: O ölçün (keşfettim

ayrıca arama yaptıktan sonra) sadece orijinal eleman boyuta uygulanır. Bir denetim şablonu, gerçekten denetimleri ekleyerek bunu değiştirirse, en iyi yöntem muhtemelen her birini ölçmek olur, ancak itiraf etmeliyim ki bu biraz dağınıklıktan daha fazlasıdır.

Derleyicinin, örneğin, yığın panelleri içine yerleştirmek için kullanması gerektiğinden, bir denetleyicinin tüm içeriğini ölçmenin bir yolu olmalıdır. Ona erişmenin bir yolu olmalı, ama şimdilik tamamen fikirlerden çıktım.

+0

Bu kodu nereden kullanıyorsunuz? Pencerenin yapıcısında mı? Eğer öyleyse, kontroller oluşturuluncaya kadar beklemeniz gerekir. – Carlo

cevap

1

Çözüldü!

Sorun benim XAML'mdaydı. Etiketimin şablonunun en üst düzeyinde, yükseklik veya genişlik alanı olmayan bir üst tuval vardı. Bu, çocukları için boyutunu değiştirmek zorunda olmadığından, sürekli olarak 0,0'ya ayarlandı. Bunu kaldırarak ve kök düğümünü, çocuklarına sığacak şekilde yeniden boyutlandırması gereken bir kenarlıkla değiştirerek, yükseklik ve genişlik alanları, Measure() çağrılarında koduma geri yüklenir ve yayılır.

3

WPF'ye oldukça yeni geldim, ama işte benim anlayışım.

Denetimleri programatik olarak güncelleştirdiğinizde veya oluşturduğunuzda, aynı zamanda ateş eden bir mekanizma yoktur (yine de benim gibi yeni başlayanlar için - daha önce windows mesaj işlemlerini bitirmiş olsam da, bu beni yakaladı ...). Güncelleştirmeler ve denetimlerin oluşturulması, ilişkili iletilerin UI gönderme sırasına sıraya girer; burada önemli nokta, bunların gelecekte bir noktada işlenecek olmasıdır. Bazı özellikler işlenen bu mesajlara bağlıdır. ActualWidth. Bu durumda iletiler denetimin oluşturulmasına ve sonra işlenen bir denetimin ilişkili özelliklerinin güncellenmesine neden olur. Eşzamansız ileti işlemenin gerçekleştiği ve bazı özelliklerin güncellenmesinden önce bu iletilerin işlenmesini beklemek zorunda olduğunuz programatik olarak denetimler oluştururken belirgin değildir. ActualWidth.

mesajları mevcut beklerseniz

o zaman güncellenmiş olacak, ActualWidth erişmeden önce işlenecek:

//These operations will queue messages on dispatch queue 
    Label lb = new Label(); 
    canvas.Children.Insert(0, lb); 

    //Queue this operation on dispatch queue 
    Dispatcher.Invoke(DispatcherPriority.Render, new Action(() => 
    { 
     //Previous messages associated with creating and adding the control 
     //have been processed, so now this happens after instead of before... 
     double width = lb.RenderSize.Width; 
     width = lb.ActualWidth; 
     width = lb.Width;  
    })); 

Güncelleme

yorumunuza yanıt olarak. Başka bir kod eklemek isterseniz, kodunuzu aşağıdaki gibi düzenleyebilirsiniz. WPF bir düzen pas yapana kadar

Label lb = new Label(); 
    canvas.Children.Insert(0, lb); 

    Dispatcher.Invoke(DispatcherPriority.Render, new Action(() => 
    { 
     //Synchronize on control rendering 
    })); 

    double width = lb.RenderSize.Width; 
    width = lb.ActualWidth; 
    width = lb.Width; 

    //Other code... 
+0

Ah, sana çok teşekkür ediyorum. Varolan iletilerin işlenip işlenmediğini kontrol etmek için kontrol etmek için bir çeşit veya boolean değeri var mı? Kontrol oluşturulduktan sonra sırayla çalışması gereken bu değerleri kontrol etmeye dayanan bir kodum var. Eğer emin değilseniz bir çeşit çözüm bulabileceğime eminim. –

19

kontrolü belirli bir boyutu yoktur olacaktır: Önemli biraz hareket memuru çağrı kontrolleri olmak render yürütür onlara bağlıdır senin kodundan önce işlenen edilene kadar beklemek sağlar vardır . Sanırım bu asenkron olarak oluyor. Kurucunuzda bunu yapıyorsanız, Yüklü etkinliği çağırabilirsiniz - düzen o zamana kadar gerçekleşecek ve yapıcıya eklediğiniz tüm denetimler boyutlandırılacaktır. Bununla birlikte, başka bir yol da, kontrolün ne boyutta olmasını istediğini hesaplamasını istemenizdir. Bunu yapmak için Measure numaralı telefonu arayın ve önerilen boyutu iletin.

lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 
Debug.WriteLine(lb.DesiredSize.Width); 

çağrı doesn Ölçülür': Bu durumda, söz konusu Tuval düzen geçişte yapılacak olup bitenleri beri, (kontrol anlam o seviyor kadar büyük olabilir) sonsuz büyüklüğünü geçmek istiyorum t kontrolün Genişlik, RenderSize veya ActualWidth değerini değiştirir. Etiketin ne boyutta olmasını istediğinizi hesaplamasını ve bu değeri DesiredSize'e (son amacı, son ölçüm çağrısının sonucunu tutmak olan bir özellik) koymasını söyler.

+0

Bu çok zarif bir çözümdür, ancak her ikisi de farklı durumlar için kullanılabilir, bu yüzden her ikisini de cevap olarak ayarlayacağım. Sanırım bu benim problemime en iyi uyuyor, teşekkürler.Bu temiz ve zarif çözüm için –

+1

+ 1. –

+0

Denetimimde, varsayılan 'tercih edilen boyut' boyutundan daha uzun olması gereken bir DataContext setine sahibim. 'Auto' yüksekliğiyle oluşturulmuş bir' UserControl 'var. Bu yanıt bu durumda benim için çalışmıyor gibi görünüyor. DataContext'i 'büyümeye' yükseltmek için ayarladıktan sonra UpdateLayout() yapmam gerekiyordu. –